Populating a tree control with Maya DAG Nodes

MItDagoffers a convenient way to iterate over the DAG nodes in any Maya scene, but not in a way that is useful for populating the visited nodes in a Windows tree control. This is because MItDaggives a linear list of nodes that make it a little bit difficult to get back the parent-child relation of the nodes. To be able to insert the nodes into a tree control we need the ability to recurse over the nodes efficiently, and MDagPathperfectly suits for this.

MDagPathoffers methods to retrieve the children count, individual child by index, and to increase/decrease the path by pushing/popping any child node to/from the end of the path. The ability to push/pop a child node from the path is very useful for the recursion. We can use these methods to visit the entire graph rooted at the given path as shown below:

 bool VisitDagPath(MDagPath& path, HWND hwndTree, HTREEITEM hTreeItem)
{
    TVINSERTSTRUCT tvi;
    tvi.hParent = hTreeItem;
    tvi.hInsertAfter = TVI_LAST;
    tvi.item.mask = TVIF_TEXT;

    // For each child
    for(unsigned int i=0, nMax = path.childCount(); i < nMax; ++i)
    {
        // Get the child object
        MObject& ChildObject = path.child(i);
        
        LPTSTR lpszName = _T("UnNamed");
        tvi.item.pszText = lpszName;
        
        // Insert a dummy node in the tree control to represent the child
        HTREEITEM hChildItem = TreeView_InsertItem(hwndTree, &tvi);

       // Now, lets visit the child object
        path.push(ChildObject);
        VisitDagPath(path, hwndTree, hChildItem);
        path.pop(1);
    }

    // Update the dummy tree node with real values of interest.
    if(path.node().hasFn(MFn::kDependencyNode))
    {
        MFnDependencyNode DepNode(path.node());
        TVITEM item;
        item.mask = TVIF_HANDLE | TVIF_TEXT;
        item.hItem = hTreeItem;
        item.pszText = (LPTSTR) MStringToTChar(DepNode.typeName());
        TreeView_SetItem(hwndTree, &item);    
    }

    return true;
}

In the above code, we are visiting the nodes in depth-first manner starting from the given path. We first create a dummy tree item to make the recursion easier, and later come back to it (after visiting all the children) to update the item text with the required details (in the above example, we use type-name of the node as the tree item text).

Invoking the VisitDagPath() is quite easy. We retrieve the root node of the scene and pass its path, along with the root item of the tree control (which is typically NULL). We use the fact that a fresh MItDag variable initially points to the root node of the scene, and using MItDag::getPath() on it retrieves the path of the root node. The code looks something like below:

 {
    // We use MItDag to retreive the path to the root object of the scene
    MItDag dagIter;

    MDagPath path;

    if(!dagIter.getPath(path))
    {
        ErrorMessage(_T("Failed while retrieving the Root node Path"));
        return false;
    }

    // Start the DAG traversal
    VisitDagPath(path, hwndTree, NULL);
}

MStringToTChar is a simple helper function to convert MString to LPCTSTR defined as below:

 inline LPCTSTR MStringToTChar(const MString& MStr)
{
#if defined(UNICODE) || defined(_UNICODE)
    return MStr.asWChar();
#else
    return MStr.asChar();
#endif
}