Traversing dependency graph problem

Hi, I need to traverse the dependency graph in search of input node of a specified api type (let it be skinCluster). Most of the tutorials online show a methot that goes like this (api 2.0):

s_list = OpenMaya.MGlobal.getActiveSelectionList()
d_node = s_list.getDependNode(0)

it = OpenMaya.MItDependencyGraph(
    d_node, OpenMaya.MItDependencyGraph.kDownstream)

while not it.isDone():
    current_item = it.currentNode()
    print(current_item.apiType())
    it.next()

As far as I know this should be the way to print out all api types of input nodes down the line (at least the shape node), however, when ran with a mesh selected (object mode) it prints out 110, which is the apiType of a transform node. And that’s it, like there was nothing more there. Am I missing something here? It looks like the method either doesn’t work like it used to, or I forgot to pass some sort of parameter. I’m using Maya 2022 btw.
Now the important thing - i need to solve it without cmds and PyMel.

In cmds theres an objectType and nodeType command - they both return different results. I’m wondering if the api call is calling nodeType essentially rather than objectType.

Also maybe you need to iterate the nodes, and not the graph?:

om.MItDependencyNodes(type)

https://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__py_ref_class_open_maya_1_1_m_it_dependency_nodes_html

https://www.programcreek.com/python/example/103923/maya.OpenMaya.MObject

apiType is a method of all(?) Maya objects, it returns an integer corresponding to a certain attribute of MFn class, while objectType and nodeType commands return strings.

And yeah, I’ve tried to use MItDependencyNodes() as well and without any luck, although I think I might have chosen a wrong node for the root :thinking:

Anyway, I tried to use MItDependencyGraph() with shape node as starting point and what I got was the given shape node (MFn.kMesh, as expected) and apiType 320, which is MFn.kShadingEngine. I think I don’t understand Maya’s DG yet…

To be clear - the goal of this is to eventually get skin cluster node from object mode selection without using any commands.

The iterator function signature lets you pass OpenMaya.MFn for this specific reason. One way to see OpenMaya.MFn is as a long enum of registered node types. If you want to simply get debug info while iterating up/down the graph, you will most likely need use a reverse map on OpenMaya.MFn. For the record, as I recall, getting a shader/material from a selected node required moving up AND down the dependency graph (don’t remember off the top of my head). When I don’t get the results expected I tend to review the graph connections in the NodeEditor to get an idea of how to move along the graph.

reverse_mfn_mapping = {v: k for k, v in OpenMaya.MFn.__dict__.items() if k.startswith('k')}
reverse_mfn_mapping[320]
>>> kShadingEngine
reverse_mfn_mapping[OpenMaya.MFn.kShadingEngine]
>>> kShadingEngine

Yes, reviewing NodeEditor/Hypergraph is precisely what I do, however I don’t get the nodes I’m looking at :confused: I have no idea how the MItDependencyGraph() method is supposed to be used. Do you pass a transform node to it? Or should I .extedndToShape() and pass a shape node as root to get to the skin cluster?

Yep. This block should do what you want. The get_mesh is a catch-all; if you pass it a skinCluster it’ll return its connected shape.

from maya.api import OpenMaya

def get_mesh(node):
    # type: (OpenMaya.MObject) -> Union[OpenMaya.MObject, None]
    """
    Returns the shape of a given node.

    :param node:
    :return:
    """
    if node.hasFn(OpenMaya.MFn.kSkinClusterFilter):
        skin_fn = OpenMayaAnim.MFnSkinCluster(node)
        nodes = skin_fn.getOutputGeometry()
        if not len(nodes):
            return None
        node = nodes[0]
    
    if not node.hasFn(OpenMaya.MFn.kDagNode):
        return None
    
    dag_fn = OpenMaya.MFnDagNode(node)
    dag_path = dag_fn.getPath()
    if dag_path.numberOfShapesDirectlyBelow():
        dag_path.extendToShape()

    mesh = dag_path.node()
    if mesh.hasFn(OpenMaya.MFn.kMesh):
        return mesh


def get_skincluster_from_node(node):
    # type: (OpenMaya.MObject) -> Union[OpenMaya.MObject, None]
    """
    Returns skin from node.

    :param node: 
    :return: 
    """
    if node.hasFn(OpenMaya.MFn.kTransform):
        node = get_mesh(node)

    if node is None:
        return None

    iterator = OpenMaya.MItDependencyGraph(node, OpenMaya.MFn.kSkinClusterFilter,
                                           OpenMaya.MItDependencyGraph.kUpstream,
                                           OpenMaya.MItDependencyGraph.kDepthFirst,
                                           OpenMaya.MItDependencyGraph.kPlugLevel)

    while not iterator.isDone():
        skin_cluster = iterator.currentNode()
        if not skin_cluster.isNull():
            return skin_cluster
2 Likes

Upstream… skin cluster is up the dg, not down… :man_facepalming: :man_facepalming: :man_facepalming:
Oh God, I feel so stupid now. Thank you, srsly, I’ve been fighting this problem for two days now :sweat_smile:
Btw. any idea why I’m getting kDagPose instead of ‘kSkinClusterFilter’ (686 instead of 682 - I’m just ctrl+Fing MFn in the docs for debug info :stuck_out_tongue: )?

Do you mean the value that you get in the maya script editor from OpenMaya.MFn.kSkinClusterFilter is not the same as that of the docs? If so, it can be the docs are not correct, or you’re simply searching in the docs for a different version of the app. That number is not necessarily constant between versions of the app and even between updates. I’ve been burned by that… always use the constant name in OpenMaya.MFn.

I mean that when I print out the node’s apiType() i get 686 right after the shape node. When I check it in the docs, it says kDagPose.

EDIT:
The graph we’re checking:

When performing traversal upstream, i get this:
image

So i t seems i get two kMeshes (296). Maybe this is the expected output of a skincluster? Another thing - why six joints in the output, when there are three?
Generally there seems to be twice the node count, when compared to the graph.

EDIT2:
Ok, so I did your reverse mapping for debug and everything seems (almost) right:
image
Seems like my docs ctrl+F lookup method isnt the most efficient one :laughing:
(still not sure about two meshes and six joints though)

EDIT3:
Okay, because there is a traversal path between the joints themselves as well (scale to inverse scale connection - I assume it’s the segmentScaleCompensate attribute).
Now everything’s clear. Thank you one more time, I really learned a lot today :wink: