Discussion:
[Maya-Python] Get all child attributes of compound node when one sibling changes
Michael Kato
2018-08-30 01:32:45 UTC
Permalink
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] <about:invalid#zClosurez>


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+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/43e35225-ada9-4f02-8055-e63231cd5a92%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Rémi Deletrain
2018-08-30 14:26:35 UTC
Permalink
Hi Michael,

You can get compound attribute like this.

def compute(self, plug, dataBlock):

# Compute only on input compound attribute
a_valid_attr = [self.attrIn_tensionColor_01,
self.attrIn_tensionIndex_01, self.attrIn_blendWeight]
if plug not in a_valid_attr:
return

# Get CompoundAttribute array handle
ah_bs_multi_input =
data.inputArrayValue(self.attrIn_blendShapeMultiInput)
i_input_count = ah_bs_multi_input.elementCount()

# Iter on element count
for i in xrange(i_input_count):

# Get compound handle
ah_bs_multi_input.jumpToElement(i)
h_bs_multi_input = ah_bs_multi_input.inputValue()

# Get compound children handle
h_tension_color = h_bs_multi_input.child(0) #
cls.attrIn_tensionColor_01
h_tension_index = h_bs_multi_input.child(0) #
cls.attrIn_tensionIndex_01
h_blend_weight = h_bs_multi_input.child(0) # cls.attrIn_blendWeight
--
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+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/eee1b335-d7ca-4e2f-acfc-2cd33d64fad8%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Rémi Deletrain
2018-08-30 14:29:35 UTC
Permalink
Oh sorry I'm make a mistake

# Get compound children handle
h_tension_color = h_bs_multi_input.child(0) # cls.attrIn_tensionColor_01
h_tension_index = h_bs_multi_input.child(1) # cls.attrIn_tensionIndex_01
h_blend_weight = h_bs_multi_input.child(2) # cls.attrIn_blendWeight
--
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+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/141d64f6-4101-431b-b349-02534273a897%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael Kato
2018-08-30 23:19:50 UTC
Permalink
Thanks for the help Remi!

Your method didn't work for me right away but was a big help. Knowing that
I can get any attribute I want inside the plug without relying on the
datablock being passed into compute was pivotal along with some of your
other examples, like getting the children of the compound node. I had to
use direct references to the MObjects themselves instead of indices,
however.

In the end I used this and it mostly works, except that sometimes the
IntArray attribute doesn't have any data in it, not sure why yet.

def compute(self, plug, dataBlock):

if plug.attribute() == self.attrIn_blendWeight:
inputAttrs = [self.attrIn_tensionColor_01, self.attrIn_tensionIndex_01, self
.attrIn_blendWeight]
multiInputArrayHandle = dataBlock.inputArrayValue( self.attrIn_blendShapeMultiInput
)

while not multiInputArrayHandle.isDone():
multiInputHandle = multiInputArrayHandle.inputValue()

tensionColorHandle = multiInputHandle.child( inputAttrs[0] )
tensionIndexHandle = multiInputHandle.child( inputAttrs[1] )
blendWeightHandle = multiInputHandle.child( inputAttrs[2] )

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:
rgbValues = self.sortRGB( tensionIndex, tensionColor )
print "Done", rgbValues
except:
print "Bad stuff"

# next!
multiInputArrayHandle.next()
Post by Michael Kato
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+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/78a5e6e2-ca1b-43e1-b2cc-a87fc0087b65%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael Kato
2018-08-30 23:24:57 UTC
Permalink
Thanks for the help Remi!

Your method didn't work for me right away but was a big help. Knowing that
I can get any attribute I want inside the compute() without relying on the
datablock being passed in was pivotal along with some of your other
examples, like getting the children of the compound attribute. I had to use
direct references to the MObjects themselves instead of indices, however.

In the end I used this and it mostly works, except that sometimes the
IntArray attribute doesn't have any data in it, not sure why yet.

def compute(self, plug, dataBlock):

if plug.attribute() == self.attrIn_blendWeight:
inputAttrs = [self.attrIn_tensionColor_01, self.attrIn_tensionIndex_01, self
.attrIn_blendWeight]
multiInputArrayHandle = dataBlock.inputArrayValue( self.attrIn_blendShapeMultiInput
)

while not multiInputArrayHandle.isDone():
multiInputHandle = multiInputArrayHandle.inputValue()

tensionColorHandle = multiInputHandle.child( inputAttrs[0] )
tensionIndexHandle = multiInputHandle.child( inputAttrs[1] )
blendWeightHandle = multiInputHandle.child( inputAttrs[2] )

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:
rgbValues = self.sortRGB( tensionIndex, tensionColor )
print "Done", rgbValues
except:
print "Bad stuff"

# next!
multiInputArrayHandle.next()
Post by Michael Kato
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+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/a2978a97-b7d9-498f-be1a-af14ec6c7065%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Rémi Deletrain
2018-08-31 07:50:45 UTC
Permalink
I gave you an example in API 1.0. There are differences between the two
APIs. I have never worked with API 2.0.

Your attributeAffects is not valid. The first argument must be an
inputAttribute ans second must be outputAttribute.
cls.attributeAffects( cls.attrIn_blendWeight, cls.attrIn_blendShapeMultiInput
)
In your code you have two inputAttribute and with this error your
imputeAttribute never affect outputAttribute.
cls.attributeAffects(cls.attrIn_blendShapeMultiInput, cls.
attrOut_colorPerVertex)

In compute you must clean attribute.
add this code at the end of compute
data.setClean(plug)
--
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+***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/023ea9fc-9a1b-444c-a753-a2b5c81cc280%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael Kato
2018-09-01 01:33:54 UTC
Permalink
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
Post by Michael Kato
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
Post by Michael Kato
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+***@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.
Loading...