Search code examples
pythonmayaskinning

maya.api.openMaya setWeight not calling right version of function


I'm not including the supplementary files that I use to track maya nodes, so if you see attr, just know that it's a way to track dags/MObjects as well as pass variables and find paths to and from attributes of those tracked nodes. The Polygon and PolygonShape are similar classes that inherit that node tracking method but are specific to those types of nodes. The code for them was further up in this file, but I'm excluding the code so as to not make this any longer than it needs to be. Suffice to say, they just tracks the polygon dag and the polygonShape MObj.

Here's my problem.

# Error: line 1: TypeError: file D:/Libraries/My Documents/maya/scripts\toolbox\Shapes.py line 332: an integer is required # 

The line 332 is the setWeight line at the bottom.

Using this code, I can get the weights and break them apart by their verts seemingly fine, but when I try to setWeights, it says that it needs an int variable. That int variable refers to a vert by vert version of the setWeight method. For some reason it's not accepting the variables I'm submitting and reverting to the wrong method. I've checked all the types going into the method. They are correct. I checked that the length of the influences and the length of the weights were correct. Since they're the right type and right length, I'm left to assume that it's something to do with the self.vertexComp, but that MObject worked correctly for the get, and I've seen versions of this setWeights that used the exact same method.

Can anyone look at this and see what is causing the setWeight to get that error:

import maya.cmds as cmds
import Attributor as attr #custom package to interact with nodes
import maya.api.OpenMaya as om
import maya.api.OpenMayaAnim as omAnim

class skinCluster(attr.node):
    outputGeometry = attr.connection("outputGeometry")

    def getInfl_IDs(self,dagObj=False,pathFirst=False):
        infDags = self.skinFn.influenceObjects()
        infIds = {}
        for x in xrange(len(infDags)):
            infPath = attr.joint(nodeName=infDags[x].fullPathName()) if dagObj else infDags[x].fullPathName()
            infId = int(self.skinFn.indexForInfluenceObject(infDags[x]))
            if pathFirst:
                infIds[infPath] = infId
            else:
                infIds[infId] = infPath
        return dict(infIds)
        # Returns something like {0:"joint1",1:"joint2"}
        # useful for figuring out what list indexes to edit with weight tools

    def __init__(self, nodeName=None, tracker=None, vertices=None, shapeNode=None):
        if not nodeName and vertices:
            nodeName = polygonShape(nodeName=vertices[0]).inMesh["in"][0]
        self.initialize(nodeName=nodeName, tracker=tracker)
        self.polygon = polygon(nodeName=self.outputGeometry["out"][0])
        self.shapeNode = shapeNode if shapeNode else polygonShape(nodeName=self.polygon.shapeNodes[0].path)
        self.skinFn = omAnim.MFnSkinCluster(self.tracker.obj)
        self.vertexComp = om.MFnSingleIndexedComponent().create(om.MFn.kMeshVertComponent)

    def getWeights(self, vertices=None):
        vertWeights = self.skinFn.getWeights(self.polygon.tracker.dag, self.vertexComp)
        weights = list(vertWeights[-2])
        infCount = vertWeights[-1]
        weights = [weights[x:x+infCount] for x in range(0,len(weights),infCount)]
        dicty = {}
        for i, weight in enumerate(weights):
            if not vertices or i in vertices:
                dicty.update({i:weight})
        return dicty
        # returns a vert weight list
        # {0:[0.1, 0.2, 0.3, 0.4], 1:[etc..]}

    def setWeights(self,values=None,normalize=True):
        vertices = values.keys() #gets all the verts that are going to be edited
        oldWeights = self.getWeights(vertices) #gets vert weights for undo
        oldValues = []
        newValues = []
        influences = self.getInfl_IDs().keys() # gets the influence indices
        for vert in vertices:
            oldValues += oldWeights[vert]   # combine weights into a list
            newValues += values[vert]  # combines the new weights to a list
        self.skinFn.setWeights(self.polygon.tracker.dag, self.vertexComp,
                               om.MIntArray(influences), om.MDoubleArray(newValues),
                               normalize=normalize, oldValues=om.MDoubleArray(oldValues))

While printing to double check types, this was the output:

<type 'OpenMaya.MDagPath'>
<type 'OpenMaya.MObject'>
<type 'OpenMaya.MIntArray'>
<type 'OpenMaya.MDoubleArray'>
<type 'bool'>
<type 'OpenMaya.MDoubleArray'>

Source Materials:

Found a post with a similar issue, but it didn't appear to help. Maybe someone else will notice why their solutions work and what I'm missing.


Solution

  • What I found with the maya.api.OpenMayaAnim setWeight function is that it's not undoable. You have to do extra steps to make it so it can be undone, and that's a pain. So instead I created something that fetches the getWeight values and uses the skinPercent command to set them.

    @property
    def weights(self):
        vertWeights = self.skinFn.getWeights(self.polygon.tracker.dag, self.vertexComp)
        weights = list(vertWeights[-2])
        infCount = vertWeights[-1]
        weights = [weights[x:x + infCount] for x in range(0, len(weights), infCount)]
        dicty = {}
        for i, weight in enumerate(weights):
            dicty.update({i: weight})
        # returns {vert1:[weightValue0,weightValue1,weightValue2..],vert2:[etc..]}
        # weight list index corresponds to influences from self.getInfl_IDs(), so it's easy to iterate weights using the index.
        return dicty
    
    @weights.setter
    def weights(self, values):
        #Will only set verts sent to it. Uses same data structure as get
        influences = self.getInfl_IDs()
        for vert, weights in values.iteritems():
            tempy = []
            for i, value in enumerate(weights):
                tempy += [(influences[i],value)]
            cmds.skinPercent(self.path, self.shapeNode.getVertName(vert), transformValue=tempy)