Thanks again Remi, unfortunately yeah I went with API 2.0 to see the state of things... so far I have no complaints except that it makes implementing other people's code harder. Things are finally working after more fiddling and some pretty disgusting hacks which I'll eventually have to fix.
I'm really not sure if this needed to be an API plugin at all but it was good practice. Here's a video! The Shape Editor was hidden somehow during capture https://imgur.com/a/X2chB0h import sys import os import re from collections import defaultdict import pymel.core as pm import maya.api.OpenMaya as om def maya_useNewAPI(): pass def pluginLoader(load): """ Load or Unload the plugin defined in this module. Parameters: load : bool : True to load the plugin, False to unload it. """ global __file__ if __file__.endswith('.pyc'): __file__ = __file__ [:-1] _, plugin = os.path.split(__file__) pluginName = plugin.split('.')[0] if load: if not pm.pluginInfo(plugin, query=True, loaded=True): pm.loadPlugin(__file__, quiet=True) else: if pm.pluginInfo(plugin, query=True, loaded=True): pm.flushUndo() pm.unloadPlugin(plugin, force=True) return pluginName def getBlendShapeMesh(): blendShapes = pm.ls( type = pm.nt.BlendShape ) for bs in blendShapes: blendShapeInputs = bs.getGeometry() return pm.PyNode( blendShapeInputs[0] ) def getBlendShapeAttrs(): blendShapes = pm.ls( type = pm.nt.BlendShape ) data = defaultdict(list) data[0] = blendShapes for bs in blendShapes: blendShapeInputs = bs.getGeometry() for bsi in blendShapeInputs: bsiNode = pm.PyNode(bsi) attrs = bsiNode.listAttr() for attr in attrs: foundColor = attr.find('_TC') > -1 foundIndex = attr.find('_TI') > -1 if foundColor or foundIndex: # regular string search fails on certain attributes attrAlias = attr.attrName().split('_')[0] data[attrAlias].append(attr) return data def connectBlendShapeNodes(): blendShapeAttrs = getBlendShapeAttrs() # load the plugin plugin = pluginLoader(True) pluginNode = pm.createNode(TensionMapBlender.nodeName) pluginAttrs = pluginNode.listAttr() blendShape = blendShapeAttrs.pop(0)[0] # TODO: support multiple blend shapes i = 0 for attrAlias, attrs in blendShapeAttrs.items(): for attr in attrs: if '_TC' in attr.attrName(): # >> is Pymel syntax to connect an attribute attr >> pluginNode.blendShapeMultiInput[i].tensionColor if '_TI' in attr.attrName(): attr >> pluginNode.blendShapeMultiInput[i].tensionIndex else: attr = pm.PyNode( blendShape + '.' + attrAlias ) attr >> pluginNode.blendShapeMultiInput[i].blendWeight i += 1 class TensionMapBlender(om.MPxNode): nodeName = 'tensionMapBlender' nodeId = om.MTypeId( 0xDAE1 ) attrIn_blendShapeMultiInput = om.MObject() attrIn_tensionColor_01 = om.MObject() attrIn_tensionIndex_01 = om.MObject() attrIn_blendWeight = om.MObject() """ attrOut_outputMesh = om.MObject() # Attribute hierarchy goes: colorPerVertex > vertexColor > vertexColorRGB >> vertexColorR,G,B attrOut_colorPerVertex = om.MObject() attrOut_vertexColor = om.MObject() attrOut_vertexColorRGB = om.MObject() attrOut_vertexAlpha = om.MObject() attrOut_colorPerFaceVertex = om.MObject() attrOut_vertexFaceColor = om.MObject() attrOut_vertexFaceColorRGB = om.MObject() attrOut_vertexFaceAlpha = om.MObject() """ def __init__(self): ''' constructor ''' om.MPxNode.__init__(self) def compute(self, plug, dataBlock): if plug == self.attrIn_blendWeight: multiInputArrayHandle = dataBlock.inputArrayValue( self.attrIn_blendShapeMultiInput ) mergedVertColors = {} while not multiInputArrayHandle.isDone(): multiInputHandle = multiInputArrayHandle.inputValue() tensionColorHandle = multiInputHandle.child( self.attrIn_tensionColor_01 ) tensionIndexHandle = multiInputHandle.child( self.attrIn_tensionIndex_01 ) blendWeightHandle = multiInputHandle.child( self.attrIn_blendWeight ) tensionColorData = tensionColorHandle.data() tensionIndexData = tensionIndexHandle.data() tensionColor = om.MFnDoubleArrayData( tensionColorData ).array() try: tensionIndex = om.MFnIntArrayData( tensionIndexData ).array() except: multiInputArrayHandle.next() continue blendWeight = blendWeightHandle.asFloat() #print tensionColor #print tensionIndex #print blendWeight try: mergedVertColors = self.mergeVertColors( mergedVertColors, tensionColor, tensionIndex ) #print "Done", mergedVertColors except: multiInputArrayHandle.next() continue # next! multiInputArrayHandle.next() # this is ultra hacky mesh = getBlendShapeMesh() for index, color in mergedVertColors.items(): #print index #print color blendedValues = [] for i in color: blendedValues.append( i * blendWeight ) blendedColor = om.MColor( blendedValues, model=om.MColor.kRGB, dataType=om. MColor.kFloat ) mesh.setVertexColor(blendedColor.getColor(), index) # for some reason setVertexColor works but setVertexColors doesn't, arg #mesh.setVertexColors( mergedVertColors.values(), mergedVertColors.keys() ) # is this plug actually dirty though? guess it can't hurt dataBlock.setClean(plug) def mergeVertColors(self, mergedColors, vertColors, vertIndices): for color, index in zip(vertColors, vertIndices): # make vectors if color < 1: color = om.MColor( [0, 0, color], model=om.MColor.kRGB, dataType=om.MColor.kFloat ) if color >= 1: color -= 1 color = om.MColor( [color, 0, 0], model=om.MColor.kRGB, dataType=om.MColor.kFloat ) if index in mergedColors: temp = mergedColors[index] + color newColor = [] # clamp values to a max of 1 for i in temp: if i >= 1: newColor.append(i) else: newColor.append(1) mergedColors[index] = newColor else: mergedColors[index] = color return mergedColors @classmethod def nodeCreator(cls): ''' creates and instance of out node class and delivers to to maya as a pointer ''' return cls() @classmethod def nodeInitializer(cls): ''' defines the input and output attributes as static variables in our plugin ''' blendShapeMultiInput = om.MFnCompoundAttribute() doubleArrayAttrFn = om.MFnGenericAttribute() intArrayAttrFn = om.MFnGenericAttribute() intAttrFn = om.MFnNumericAttribute() ''' input attributes ''' cls.attrIn_blendShapeMultiInput = blendShapeMultiInput.create( ' blendShapeMultiInput', 'mai' ) cls.attrIn_tensionColor_01 = doubleArrayAttrFn.create( 'tensionColor', 'tc' ) cls.attrIn_tensionIndex_01 = intArrayAttrFn.create( 'tensionIndex', 'ti' ) cls.attrIn_blendWeight = intAttrFn.create( 'blendWeight', 'bw', om. MFnNumericData.kFloat ) doubleArrayAttrFn.addDataType( om.MFnData.kDoubleArray ) intArrayAttrFn.addDataType( om.MFnData.kIntArray ) blendShapeMultiInput.array = True blendShapeMultiInput.addChild( cls.attrIn_tensionColor_01 ) blendShapeMultiInput.addChild( cls.attrIn_tensionIndex_01 ) blendShapeMultiInput.addChild( cls.attrIn_blendWeight ) cls.addAttribute( cls.attrIn_blendShapeMultiInput ) """ typedAttribute = om.MFnTypedAttribute() cls.attrOut_outputMesh = typedAttribute.create("outMesh", "om", om.MFnData.kMesh, om.MObject.kNullObj ) cls.addAttribute( cls.attrOut_outputMesh ) ### keeping this around because it took a lot of work to make ### ''' output attributes ''' numAttrFn = om.MFnNumericAttribute() out_compAttrFn_parent = om.MFnCompoundAttribute() out_compAttrFn_child1 = om.MFnCompoundAttribute() out_compAttrFn_child2 = om.MFnCompoundAttribute() cls.attrOut_colorPerVertex = out_compAttrFn_parent.create( 'colorPerVertex', 'cpvo' ) cls.attrOut_vertexColor = out_compAttrFn_child1.create( 'vertexColor', 'vco' ) cls.attrOut_vertexColorRGB = numAttrFn.createColor( 'vertexColorRGB', 'vcrgbo') cls.attrOut_vertexAlpha = numAttrFn.create( 'vertexAlpha', 'vao01', om.MFnNumericData.kFloat) cls.attrOut_vertexFaceColor = out_compAttrFn_child2.create( 'vertexFaceColor', 'vfco' ) cls.attrOut_vertexFaceColorRGB = numAttrFn.createColor( 'vertexFaceColorRGB', 'vcfrgbo') cls.attrOut_vertexFaceAlpha = numAttrFn.create( 'vertexFaceAlpha', 'vfao', om.MFnNumericData.kFloat) out_compAttrFn_parent.addChild( cls.attrOut_vertexColor ) out_compAttrFn_child1.array = True out_compAttrFn_child1.addChild( cls.attrOut_vertexColorRGB ) out_compAttrFn_child1.addChild( cls.attrOut_vertexAlpha ) out_compAttrFn_child1.addChild( cls.attrOut_vertexFaceColor ) out_compAttrFn_child2.array = True out_compAttrFn_child2.addChild( cls.attrOut_vertexFaceColorRGB ) out_compAttrFn_child2.addChild( cls.attrOut_vertexFaceAlpha ) cls.addAttribute( cls.attrOut_colorPerVertex ) """ cls.attributeAffects( cls.attrIn_blendWeight, cls.attrIn_blendShapeMultiInput ) def initializePlugin( mobject ): ''' initliize the plug-in''' mPlugin = om.MFnPlugin( mobject ) try: mPlugin.registerNode( TensionMapBlender.nodeName, TensionMapBlender.nodeId, TensionMapBlender.nodeCreator, TensionMapBlender.nodeInitializer ) print "Loaded plugin" except: sys.stderr.write( 'Failed to register node: ' + TensionMapBlender.nodeName ) raise def uninitializePlugin( mobject ): ''' uninitialize the plugin''' mPlugin = om.MFnPlugin( mobject ) try: mPlugin.deregisterNode( TensionMapBlender.nodeId ) print "Unloaded plugin" except: sys.stderr.write( 'Failed to deregister node: ' + TensionMapBlender.nodeName ) raise On Wednesday, August 29, 2018 at 6:32:45 PM UTC-7, Michael Kato wrote: > > Hi all, > > I've having a load of trouble with this. I've managed to get some complex > (for me anyway) compound attributes set up and connected to objects in my > scene, but right now I'm unable to get the "Blend Shape Multi Input" > attribute to arrive as a plug to the compute() method in my scripted node. > > So I resorted to some trickery by looking for the "blendWeight" attribute > instead when it changes, then getting om.MDataPlug.parent() which is the > compound node, then re-getting the children of that compound node and their > data. > > My attributes look like this > > [image: node.PNG] > > > All of this works except for one crucial thing, calling asMDataHandle() > childData = childPlug.asMDataHandle().data() > > This seems to force the recompute of some things and sends my plugin into > an infinite loop. Is there a better way to do this? > > Code attached, it should work in maya 2018 (possibly earlier) in a scene > with some blendshapes in it if you call connectBlendShapeNodes() > > Thanks for any help! > -- You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group. To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/0c583d07-e6d7-4bcd-93ba-5853de1f7a50%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.