DAG walking example

 
 
 

The following example is the scanDagSyntaxCmd example. It demonstrates iterating through the DAG in either a depth first, or a breadth first manner. This code makes a good basis for many DAG walking plug-ins, in particular those written as file translators.

As with previous examples, the list of include files is omitted for brevity. See the scanDagSyntaxCmd.cpp file in devkit/plug-ins for the complete example.

class scanDagSyntax: public MPxCommand
{
 public:
     scanDagSyntax() {};
     virtual ~scanDagSyntax();
     static void* creator();
     static MSyntax newSyntax();
     virtual MStatus doIt( const MArgList& );
     //This is a simple example so the undoIt() and redoIt() methods are not implemented. 
 private:
     MStatus parseArgs( const MArgList& args,
     MItDag::TraversalType& traversalType,
     MFn::Type& filter, bool & quiet);
     MStatus doScan( const MItDag::TraversalType traversalType,
     MFn::Type filter, bool quiet);
     void printTransformData(const MDagPath& dagPath, bool quiet);
};

scanDagSyntax::~scanDagSyntax() {}
void* scanDagSyntax::creator()
{
    return new scanDagSyntax;
}

MSyntax scanDagSyntax::newSyntax()
{
    MSyntax syntax;
    syntax.addFlag(kBreadthFlag, kBreadthFlagLong);
    syntax.addFlag(kDepthFlag, kDepthFlagLong);
    syntax.addFlag(kCameraFlag, kCameraFlagLong);
    syntax.addFlag(kLightFlag, kLightFlagLong);
    syntax.addFlag(kNurbsSurfaceFlag, kNurbsSurfaceFlagLong);
    syntax.addFlag(kQuietFlag, kQuietFlagLong);
    return syntax;
}

MStatus scanDagSyntax::doIt( const MArgList& args )
{
     MItDag::TraversalType traversalType = MItDag::kDepthFirst;
     MFn::Type filter = MFn::kInvalid;
     MStatus status;
     bool quiet = false;

The DAG iterator being used later can be set to only iterate across objects of a particular type (for example cameras). If the filter mode is set to MFn::kInvalid, no filtering will be done and all DAG nodes will be iterated across.

     status = parseArgs ( args, traversalType, filter, quiet );
     if (!status)
         return status;
     return doScan( traversalType, filter, quiet);
};
//The doIt() method is simply calling a few auxiliary methods which do the real work.
MStatus scanDagSyntax::parseArgs( const MArgList& args,
                                  MItDag::TraversalType& traversalType,
                                  MFn::Type& filter,
                                  bool & quiet)
{
    MStatus stat;
    MArgDatabase argData(syntax(), args);
    MString arg;
    if (argData.isFlagSet(kBreadthFlag))
        traversalType = MItDag::kBreadthFirst;
    else if (argData.isFlagSet(kDepthFlag))
        traversalType = MItDag::kDepthFirst;
    if (argData.isFlagSet(kCameraFlag))
        filter = MFn::kCamera;
    else if (argData.isFlagSet(kLightFlag))
        filter = MFn::kLight;
    else if (argData.isFlagSet(kNurbsSurfaceFlag))
        filter = MFn::kNurbsSurface;
    if (argData.isFlagSet(kQuietFlag))
        quiet = true;
    return stat;
}

The DAG iterator can either iterate across the DAG depth first or breadth first. This simple example only filters on cameras, lights, and NURBS surfaces, but it is possible to iterate across any type in MFn::Type.

MStatus scanDagSyntax::doScan( const MItDag::TraversalType traversalType,
                               MFn::Type filter,
                               bool quiet)
{ 

This method will do all the real work of this command. It uses the traversal type (depth or breadth first) and the filter type to initialize an MItDag (a DAG iterator) to walk across the DAG.

    MStatus status;
    MItDag dagIterator( traversalType, filter, &status);

The DAG iterator is initialized looking at the DAG. It will walk the DAG downwards.

    if ( !status) {
        status.perror("MItDag constructor");
        return status;
    }
 // Scan the entire DAG and output the name and depth of each node
    if (traversalType == MItDag::kBreadthFirst)
        if (!quiet)
            cout << endl << "Starting Breadth First scan of the Dag";
        else
    if (!quiet)
        cout << endl << "Starting Depth First scan of the Dag";

Breadth first walking of the DAG means that siblings will be visited before children, while depth first means that children will be visited before siblings.

    switch (filter) {
    case MFn::kCamera:
        if (!quiet)
            cout << ": Filtering for Cameras\n";
        break;
    case MFn::kLight:
        if (!quiet)
            cout << ": Filtering for Lights\n";
        break;
    case MFn::kNurbsSurface:
        if (!quiet)
            cout << ": Filtering for Nurbs Surfaces\n";
        break;
    default:
        cout << endl;
    }
    int objectCount = 0;
    for ( ; !dagIterator.isDone(); dagIterator.next() ) {
        MDagPath dagPath;
        status = dagIterator.getPath(dagPath);
        if ( !status ) {
            status.perror("MItDag::getPath");
            continue;
        }

MItDag::getPath() gets the reference to the object that the iterator is currently on. This DAG path can then be used in a function set to operate on the object. In general it is not a good idea to rearrange the DAG from with an iterator.

        MFnDagNode dagNode(dagPath, &status);
        if ( !status ) {
            status.perror("MFnDagNode constructor");
            continue;
        }
        if (!quiet)
            cout << dagNode.name() << ": " << dagNode.typeName() << endl;
        if (!quiet)
            cout << " dagPath: " << dagPath.fullPathName() << endl;
        objectCount += 1;
        if (dagPath.hasFn(MFn::kCamera)) {

This determines if the object the iterator is currently visiting is a camera or not, and if it is, the following code outputs camera specific information.

            MFnCamera camera (dagPath, &status);
            if ( !status ) {
                status.perror("MFnCamera constructor");
                continue;
            }
            // Get the translation/rotation/scale data
            printTransformData(dagPath, quiet);
            // Extract some interesting Camera data
            if (!quiet)
            {
                cout << " eyePoint: "
                     << camera.eyePoint(MSpace::kWorld) << endl;
                cout << " upDirection: "
                     << camera.upDirection(MSpace::kWorld) << endl;
                cout << " viewDirection: "
                     << camera.viewDirection(MSpace::kWorld) << endl;
                cout << " aspectRatio: " << camera.aspectRatio() << endl;
                cout << " horizontalFilmAperture: "
                     << camera.horizontalFilmAperture() << endl;
                cout << " verticalFilmAperture: "
                     << camera.verticalFilmAperture() << endl;
            }
        } 
        else if (dagPath.hasFn(MFn::kLight)) {

If the object is a light, this code outputs light specific information.

            MFnLight light (dagPath, &status);
            if ( !status ) {
                    status.perror("MFnLight constructor");
                    continue;
            }
            // Get the translation/rotation/scale data
            printTransformData(dagPath, quiet);
            // Extract some interesting Light data
            MColor color;
            color = light.color();
            if (!quiet)
            {
                cout << " color: ["
                << color.r << ", "
                << color.g << ", "
                << color.b << "]\n";
            }
            color = light.shadowColor();
            if (!quiet)
            {
                cout << " shadowColor: ["
                << color.r << ", "
                << color.g << ", "
                << color.b << "]\n";
                cout << " intensity: " << light.intensity() << endl;
            }
        } 
        else if (dagPath.hasFn(MFn::kNurbsSurface)) {
            //Finally, if the object is a NURBS surface, surface specific information is output.
            MFnNurbsSurface surface (dagPath, &status);
            if ( !status ) {
                status.perror("MFnNurbsSurface constructor");
                continue;
            }
            // Get the translation/rotation/scale data
            printTransformData(dagPath, quiet);
            // Extract some interesting Surface data
            if (!quiet)
            {
                cout << " numCVs: "
                     << surface.numCVsInU()
                     << " * "
                     << surface.numCVsInV()
                     << endl;
                cout << " numKnots: "
                     << surface.numKnotsInU()
                     << " * "
                     << surface.numKnotsInV()
                     << endl;
                cout << " numSpans: "
                     << surface.numSpansInU()
                     << " * "
                     << surface.numSpansInV()
                     << endl;
            }
        } 
        else {

For any other type of DAG node, just the transformation information is printed.

            // Get the translation/rotation/scale data
            printTransformData(dagPath, quiet);
        }
    }
    if (!quiet)
    {
        cout.flush();
    }
    setResult(objectCount);
    return MS::kSuccess;
}

void scanDagSyntax::printTransformData(const MDagPath& dagPath, bool quiet)
{
    //This method simply determines the transformation information on the DAG node and prints it out.
    MStatus status;
    MObject transformNode = dagPath.transform(&status);
    // This node has no transform - i.e., it’s the world node
    if (!status && status.statusCode () == MStatus::kInvalidParameter)
        return;
    MFnDagNode transform (transformNode, &status);
    if (!status) {
        status.perror("MFnDagNode constructor");
        return;
    }
    MTransformationMatrix matrix (transform.transformationMatrix());
    if (!quiet)
    {
        cout << " translation: " << matrix.translation(MSpace::kWorld)
            << endl;
    }
    double threeDoubles[3];
    MTransformationMatrix::RotationOrder rOrder;
    matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld);
    if (!quiet)
    {
        cout << " rotation: ["
             << threeDoubles[0] << ", "
             << threeDoubles[1] << ", "
             << threeDoubles[2] << "]\n";
    }
    matrix.getScale (threeDoubles, MSpace::kWorld);
    if (!quiet)
    {
        cout << " scale: ["
             << threeDoubles[0] << ", "
             << threeDoubles[1] << ", "
             << threeDoubles[2] << "]\n";
    }
}

MStatus initializePlugin( MObject obj )
{ 
    MStatus status;
    MFnPlugin plugin ( obj, "Autodesk - Example", "2.0", "Any" );
    status = plugin.registerCommand( "scanDagSyntax", 
    scanDagSyntax::creator,
    scanDagSyntax::newSyntax ); 
    return status;
}

MStatus uninitializePlugin( MObject obj )
{
    MStatus status;
    MFnPlugin plugin( obj );
    status = plugin.deregisterCommand( "scanDagSyntax" );
    return status;
}

The plug-in finishes with the usual initializePlugin and uninitializePlugin methods.

This plug-in can easily be modified for use as a file translator, or any other type of plug-in which needs to visit the DAG nodes in the model.

Creative Commons License Except where otherwise noted, this work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License