DAG = Directed Acyclic Graph

DAG je orientovaný acyklický graf používaný Mayou pro reprezentaci scény. DAG obsahuje dva typy uzlů (DAG nodes) - trasformace a geometrické objekty (shapes). Transformační uzly slouží primárně pro uložení transformací (posunutí, otočení, změna měřítka, atd.). Tyto uzly mohou mít syny, kterými mohou být opět transformační uzly nebo jeden geom. objekt. Uzly geom. objektů v sobě uchovávají odkaz na nějakou geometrii a nemohou mít syny. Obecně lze říci, že DAG popisuje postup umisťování geom. objektu do scény. Kdykoliv vytvoříte nějaký geometrický objekt Maya automaticky vytvoří i nadřazený transformační uzel, který popisuje umístění objektu ve scéně. Geom. objekt je synem nadřazeného transformačního uzlu.

Uzly DAG mohou také mít více otců - v takovém případě existuje více instancí tohoto uzlu - pro každého otce jedna. Např. DAG na obrázku vlevo reprezentuje dvě instance geom. objektu nazvaného Leaf. Na první instanci se aplikují transformace Transform1 a Transform3 a na druhou instanci transformace Transform2 a Transform3.

Pro práci s uzly DAG grafu můžete využít následující třídy:

Poloha každého uzlu v DAGu je jednoznačně určena pomocí cesty - třída MDagPath. Cesta je sekvencí transformačních uzlů zakončená danným uzlem. Např. cesta k první instanci uzlu Leaf je Transform1|Transform3|Leaf a cesta k druhé instanci Leafu je Transform2|Transform3|Leaf.

Chcete-li modifikovat některý uzel DAGu (nebo provádět nějaké operace s uzlem ve světových souřadnicích) nestačí znát pouze referenci na příslušný uzel (třída MObject), ale musíte také znát správnou cestu k tomuto uzlu, protože teprve až ta jednoznačně identifikuje instanci uzlu. Většina operací pracujících s uzly vyžaduje zadání jak reference na MObject tak cesty k tomuto objektu v DAGu. V závislosti na typu posledního uzlu v cestě lze aplikovat buď function sety pracující s tranformacemi nebo function sety pracující s geometríí určenou typem uzlu. Transformační matice můžete z transformačních uzlů vytáhnout pomocí následujících dvou operací:

Jako příklad si zde uvedeme jednoduchý plug-in, který bude procházet DAG do hloubky nebo do šířky a současně bude vypisovat jména uzlů reprezentujících nějaký světelný zdroj ve scéně.


class scanDagSyntax: public MPxCommand {

public:

  scanDagSyntax() {};
  virtual ~scanDagSyntax();
  static void* creator();
  static MSyntax newSyntax();
  virtual MStatus doIt( const MArgList& );

private:

  // Zpracování parametrů příkazové řádky.
  MStatus parseArgs( const MArgList& args, MItDag::TraversalType& traversalType, MFn::Type& filter);

  // Procházení DAGu - do hloubky nebo do šířky.
  MStatus doScan( const MItDag::TraversalType traversalType, MFn::Type filter);

  // Uživatelská metoda, která vypíše informace o transformacích.
  void printTransformData(const MDagPath& dagPath);

};

// Konstruktor třídy.
scanDagSyntax::~scanDagSyntax() {}

// Metoda pro vytváření instancí této třídy.
void* scanDagSyntax::creator() {
    return new scanDagSyntax;
}

#define kBreadthFlag     "-b"
#define kBreadthFlagLong "-breadthFirst"
#define kDepthFlag       "-d"
#define kDepthFlagLong   "-depthFirst"

// Definice parametrů příkazové řádky.
MSyntax scanDagSyntax::newSyntax() {
 MSyntax syntax;

  syntax.addFlag(kBreadthFlag, kBreadthFlagLong);
  syntax.addFlag(kDepthFlag, kDepthFlagLong);

  return syntax;
}

// Provede akci požadovanou po plug-inu.
MStatus scanDagSyntax::doIt( const MArgList& args ) {

 MItDag::TraversalType traversalType = MItDag::kDepthFirst;
 MFn::Type filter = MFn::kInvalid; // počáteční nastavení filtru - bez filtrování
 MStatus status;

  status = parseArgs ( args, traversalType, filter );
  if (!status)
    return status;

  return doScan( traversalType, filter );
};

// Zpracování parametrů příkazové řádky.
MStatus scanDagSyntax::parseArgs(const MArgList& args, MItDag::TraversalType& traversalType, MFn::Type& filter) {
 MStatus stat;
 MArgDatabase argData(syntax(), args);

  if (argData.isFlagSet(kBreadthFlag))
      traversalType = MItDag::kBreadthFirst;
  else 
    if (argData.isFlagSet(kDepthFlag))
      traversalType = MItDag::kDepthFirst;

  filter = MFn::kLight;
        
  return stat;
}

// Vlastní procházení DAGu.
MStatus scanDagSyntax::doScan( const MItDag::TraversalType traversalType, MFn::Type filter, bool quiet) { 
 MStatus status;

 // Inicializace iterátoru pro procházení DAGu od shora dolů.
 MItDag dagIterator(traversalType, filter, &status);

  if ( !status) {
    status.perror("MItDag constructor");
    return status;
  }

  // Procházení DAGu s vypisováním jmen uzlů a jejich hloubky.

  if (traversalType == MItDag::kBreadthFirst)
    cout << endl << "Starting Breadth First scan of the Dag";
  else
    cout << endl << "Starting Depth First scan of the Dag";

 int objectCount = 0;

  // Procházení DAGu pomocí iterátoru.
  for ( ; !dagIterator.isDone(); dagIterator.next() ) {

   MDagPath dagPath;

    // MItDag::getPath() vrací cestu k objektu na kterém právě stojí iterátor.
    // Není dobrý nápad měnit DAG během použití iterátoru.
    status = dagIterator.getPath(dagPath);
    if ( !status ) {
      status.perror("MItDag::getPath");
      continue;
    }

   MFnDagNode dagNode(dagPath, &status);
    if ( !status ) {
      status.perror("MFnDagNode constructor");
      continue;
    }

    cout << dagNode.name() << ": " << dagNode.typeName() << endl;
    cout << "  dagPath: " << dagPath.fullPathName() << endl;

    objectCount += 1;

    if (dagPath.hasFn(MFn::kLight)) { // je-li uzel skutečně světlo, pak pokračuj
                                      // zde je tento test zbytečný.

   MFnLight light (dagPath, &status);
    if ( !status ) {
      status.perror("MFnLight constructor");
      continue;
    }

    // Vypiš transformace.
    printTransformData(dagPath);

    // Výpis některých informací o světle.
   MColor color;

    color = light.color();
    cout << "  color: [" << color.r << ", " << color.g << ", " << color.b << "]\n";
    color = light.shadowColor();
    cout << "  shadowColor: [" << color.r << ", " << color.g << ", " << color.b << "]\n";
    cout << "  intensity: " << light.intensity() << endl;

    cout.flush();
  }
  setResult(objectCount); // Výsledkem příkazu je počet zpracovaných světel.

  return MS::kSuccess;
}

// Následující metoda vypíše informace o transformačním uzlu pro světlo určené dagPath.
void scanDagSyntax::printTransformData(const MDagPath& dagPath, bool quiet) {
 MStatus status;
 // dagPath::transform() vrací nadřazený transformační uzel.
 MObject transformNode = dagPath.transform(&status);

  // Tento uzel nemá transformační matici
  if (!status && status.statusCode () == MStatus::kInvalidParameter)
    return;

 MFnDagNode transform (transformNode, &status);

  if (!status) {
    status.perror("MFnDagNode constructor");
    return;
  }

 // Vytažení transformační matice z uzlu.
 MTransformationMatrix matrix (transform.transformationMatrix());

  cout << "  translation: " << matrix.translation(MSpace::kWorld) << endl;

 double threeDoubles[3];
 MTransformationMatrix::RotationOrder rOrder;

  matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld);
  cout << "  rotation: [" << threeDoubles[0] << ", " << threeDoubles[1] << ", " << threeDoubles[2] << "]\n";
    
  matrix.getScale (threeDoubles, MSpace::kWorld);
  cout << "  scale: [" << threeDoubles[0] << ", " << threeDoubles[1] << ", " << threeDoubles[2] << "]\n";
}

// Metoda volaná při inicializaci plug-inu.
MStatus initializePlugin( MObject obj ) { 
 MStatus status;

  MFnPlugin plugin ( obj, "Alias - Example", "2.0", "Any" );
  status = plugin.registerCommand( "scanDagSyntax", 
    scanDagSyntax::creator,
    scanDagSyntax::newSyntax ); 
    
  return status;
}

// Metoda volaná při odstraňování plug-inu z Mayi.
MStatus uninitializePlugin( MObject obj ) {
 MStatus status;

  MFnPlugin plugin( obj );
  status = plugin.deregisterCommand( "scanDagSyntax" );

  return status;
}