Hi all - so I'm making a node that I need to update when it's worldSpace matrix changes - so I thought that MFnAttribute.setWorldSpace would be exactly what I was looking for.  However, I can't seem to make it work - the attribute never seems to get set dirty!

Here's the relevant sections from my test plugin (it's written in python):

compute and initialize methods

def compute(self, plug, dataBlock):
    print "compute called for:", plug.name()
    if (plug == self.worldX):
        if plug.isElement():
            arrayIndex = plug.logicalIndex()
            print "got worldX[%d]" % arrayIndex
        else:
            print "got worldX - uses default index 0"
            arrayIndex = 0
        outputArrayHandle = dataBlock.outputArrayValue( self.worldX )
        builder = outputArrayHandle.builder();
        outHandle = builder.addElement( arrayIndex );
        outHandle.setFloat(worldMatrix(3,0))
        return om.MStatus.kSuccess
    return om.MStatus.kUnknownParameter
 
@classmethod
def initialize(cls):
    nAttr = om.MFnNumericAttribute()
 
    # worldX
    cls.worldX = nAttr.create("worldX", "wx", om.MFnNumericData.kFloat, 0.0 )
    nAttr.setArray(1)
    nAttr.setWorldSpace(1)
    print "set world space for worldX"
    nAttr.setUsesArrayDataBuilder(1)
    cls.addAttribute( cls.worldX )

Here's what I was using to test it:

test code

import pymel.core as pm
 
newFile(f=1)
nodeType = 'WorldXNode'
plug = nodeType + '.py'
if pm.pluginInfo(plug, q=1, loaded=1):
    pm.unloadPlugin(plug)
pm.loadPlugin(plug)
 
tn = pm.createNode(nodeType)
pm.select(None)
tn.hide()
nodeName = tn.name()
 
# get the worldX - this SHOULD trigger a compute call for .worldX... but it doesn't
print "initial worldX get:", pm.getAttr(nodeName + '.worldX')
print "initial worldX[0] get:", pm.getAttr(nodeName + '.worldX[0]')
 
# set the parent translate to change world x
tn.getParent().tx.set(12)
print "set parent tx..."
 
# make sure that worldX is indeed set to be a worldSpace attr
attr = pm.api.MFnAttribute(tn.worldX.__apimplug__().attribute())
print "is worldX worldSpace?", attr.isWorldSpace()
 
# since we changed the worldMatrix, worldX should be dirty - but it isn't
print "worldX dirty?", pm.isDirty(nodeName + '.worldX', d=1), pm.isDirty(nodeName + '.worldX', c=1)
print "worldX[0] dirty?", pm.isDirty(nodeName + '.worldX[0]', d=1), pm.isDirty(nodeName + '.worldX[0]', c=1)
 
# query worldX again - since worldMatrix was changed, should trigger compute again
print "final worldX get:", pm.getAttr(nodeName + '.worldX')
print "final worldX[0] get:", pm.getAttr(nodeName + '.worldX')
 
# see what we SHOULD be getting back
print "true worldX:", tn.getParent().getTranslation(space='world')[0]
 
pm.dgDirty(nodeName + '.worldX[0]')
  • created

    Jul '12
  • last reply

    Jul '12
  • 1

    reply

  • 2.0k

    views

  • 1

    user

Hmm... attachments seem to be giving me problems. Well, here's the full text of the plugin:

WorldXNode.py

import maya.OpenMaya as om
import maya.OpenMayaMPx as mpx
 
class WorldXNode(mpx.MPxLocatorNode):
    '''Test plugin node
    '''
    _typeId = om.MTypeId(0x9FF0F)
    _name = 'WorldXNode'
    _typeEnum = mpx.MPxNode.kLocatorNode
 
    def compute(self, plug, dataBlock):
        print "compute called for:", plug.name()
        if (plug == self.worldX):
            if plug.isElement():
                arrayIndex = plug.logicalIndex()
                print "got worldX[%d]" % arrayIndex
            else:
                print "got worldX - uses default index 0"
                arrayIndex = 0
            outputArrayHandle = dataBlock.outputArrayValue( self.worldX )
            builder = outputArrayHandle.builder();
            outHandle = builder.addElement( arrayIndex );
            outHandle.setFloat(worldMatrix(3,0))
            return om.MStatus.kSuccess
        return om.MStatus.kUnknownParameter
 
    @classmethod
    def initialize(cls):
        nAttr = om.MFnNumericAttribute()
 
        # worldX
        cls.worldX = nAttr.create("worldX", "wx", om.MFnNumericData.kFloat, 0.0 )
        nAttr.setArray(1)
        nAttr.setWorldSpace(1)
        print "set world space for worldX"
        nAttr.setUsesArrayDataBuilder(1)
        cls.addAttribute( cls.worldX )
 
    @classmethod
    def create(cls):
        return mpx.asMPxPtr( cls() )
 
    @classmethod
    def register(cls, plugin):
        mplugin = mpx.MFnPlugin(plugin)
        mplugin.registerNode( cls._name, cls._typeId, cls.create, cls.initialize, cls._typeEnum )
 
    @classmethod
    def deregister(cls, plugin):
        mplugin = mpx.MFnPlugin(plugin)
        mplugin.deregisterNode( cls._typeId )
 
 
## initialize the script plug-in
def initializePlugin(mobject):
    WorldXNode.register(mobject)
 
# uninitialize the script plug-in
def uninitializePlugin(mobject):
    WorldXNode.deregister(mobject)