Panda3D

mayaCopy.cxx

00001 // Filename: mayaCopy.cxx
00002 // Created by:  drose (10May02)
00003 // Modified 19Mar10 by ETC PandaSE team (see
00004 //   header comment for mayaToEgg.cxx for more details)
00005 //
00006 ////////////////////////////////////////////////////////////////////
00007 //
00008 // PANDA 3D SOFTWARE
00009 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00010 //
00011 // All use of this software is subject to the terms of the revised BSD
00012 // license.  You should have received a copy of this license along
00013 // with this source code in a file named "LICENSE."
00014 //
00015 ////////////////////////////////////////////////////////////////////
00016 
00017 #include "mayaCopy.h"
00018 #include "config_maya.h"
00019 #include "cvsSourceDirectory.h"
00020 #include "mayaShader.h"
00021 #include "dcast.h"
00022 #ifdef _WIN32
00023   #include "pystub.h"
00024 #endif
00025 
00026 #include "pre_maya_include.h"
00027 #include <maya/MStringArray.h>
00028 #include <maya/MGlobal.h>
00029 #include <maya/MFileIO.h>
00030 #include <maya/MItDag.h>
00031 #include <maya/MFnDagNode.h>
00032 #include <maya/MFnNurbsSurface.h>
00033 #include <maya/MFnMesh.h>
00034 #include <maya/MObject.h>
00035 #include <maya/MDagPath.h>
00036 #include <maya/MIntArray.h>
00037 #include "post_maya_include.h"
00038 
00039 ////////////////////////////////////////////////////////////////////
00040 //     Function: MayaCopy::Constructor
00041 //       Access: Public
00042 //  Description:
00043 ////////////////////////////////////////////////////////////////////
00044 MayaCopy::
00045 MayaCopy() {
00046   set_program_description
00047     ("mayacopy copies one or more Maya .mb files into a "
00048      "CVS source hierarchy.  "
00049      "Rather than copying the named files immediately into the current "
00050      "directory, it first scans the entire source hierarchy, identifying all "
00051      "the already-existing files.  If the named file to copy matches the "
00052      "name of an already-existing file in the current directory or elsewhere "
00053      "in the hierarchy, that file is overwritten.  Other .mb files, as "
00054      "well as texture files, that are externally referenced by the "
00055      "named .mb file(s) are similarly copied.");
00056 
00057   clear_runlines();
00058   add_runline("[opts] file.mb [file.mb ... ]");
00059 
00060   add_option
00061     ("keepver", "", 0,
00062      "Don't attempt to strip the Maya version number from the tail of the "
00063      "source filename before it is copied into the tree.",
00064      &CVSCopy::dispatch_none, &_keep_ver);
00065 
00066   /*
00067   add_option
00068     ("rp", "replace_prefix", 80,
00069      "use these prefixes when replacing reference with the recently copied file from the  "
00070      "source filename before it is copied into the tree.",
00071      &CVSCopy::dispatch_vector_string, NULL, &_replace_prefix);
00072   */
00073 
00074   add_option
00075     ("omittex", "", 0,
00076      "Character animation files do not need to copy the texures. "
00077      "This option omits the textures of the models to be re-mayacopied",
00078      &CVSCopy::dispatch_none, &_omit_tex);
00079 
00080   add_option
00081     ("omitref", "", 0,
00082      "Character animation files do not need to copy internal file references. "
00083      "This option omits the references of the models to be re-mayacopied",
00084      &CVSCopy::dispatch_none, &_omit_ref);
00085 
00086   add_option
00087     ("ma", "", 0,
00088      "Write a .ma file instead of a .mb file (regardless of input type)",
00089      &CVSCopy::dispatch_none, &_maya_ascii);
00090 
00091   add_path_replace_options();
00092 }
00093 
00094 ////////////////////////////////////////////////////////////////////
00095 //     Function: MayaCopy::run
00096 //       Access: Public
00097 //  Description:
00098 ////////////////////////////////////////////////////////////////////
00099 void MayaCopy::
00100 run() {
00101   _maya = MayaApi::open_api(_program_name);
00102   if (!_maya->is_valid()) {
00103     nout << "Unable to initialize Maya.\n";
00104     exit(1);
00105   }
00106 
00107   SourceFiles::iterator fi;
00108   for (fi = _source_files.begin(); fi != _source_files.end(); ++fi) {
00109     _curr_idx = 0;
00110     ExtraData ed;
00111     ed._type = FT_maya;
00112 
00113     CVSSourceTree::FilePath path = import(*fi, &ed, _model_dir);
00114     if (!path.is_valid()) {
00115       nout << "\nUnable to copy, aborting!\n\n";
00116       exit(1);
00117     }
00118   }
00119 }
00120 
00121 ////////////////////////////////////////////////////////////////////
00122 //     Function: MayaCopy::copy_file
00123 //       Access: Protected, Virtual
00124 //  Description: Called by import() if verify_file() indicates that a
00125 //               file needs to be copied.  This does the actual copy
00126 //               of a file from source to destination.  If new_file is
00127 //               true, then dest does not already exist.
00128 ////////////////////////////////////////////////////////////////////
00129 bool MayaCopy::
00130 copy_file(const Filename &source, const Filename &dest,
00131           CVSSourceDirectory *dir, void *extra_data, bool new_file) {
00132   ExtraData *ed = (ExtraData *)extra_data;
00133   switch (ed->_type) {
00134   case FT_maya:
00135     return copy_maya_file(source, dest, dir);
00136 
00137   case FT_texture:
00138     if (_omit_tex) {
00139       return true;
00140     }
00141     return copy_texture(source, dest, dir);
00142   }
00143 
00144   nout << "Internal error: invalid type " << (int)ed->_type << "\n";
00145   return false;
00146 }
00147 
00148 ////////////////////////////////////////////////////////////////////
00149 //     Function: MayaCopy::filter_filename
00150 //       Access: Protected, Virtual
00151 //  Description: Given a source filename (including the basename only,
00152 //               without a dirname), return the appropriate
00153 //               corresponding filename within the source directory.
00154 //               This may be used by derived classes to, for instance,
00155 //               strip a version number from the filename.
00156 ////////////////////////////////////////////////////////////////////
00157 string MayaCopy::
00158 filter_filename(const string &source) {
00159   if (_keep_ver) {
00160     return source;
00161   }
00162 
00163   size_t dot = source.rfind('.');
00164   size_t underscore = source.rfind("_v", dot);
00165   if ((underscore != string::npos) && !isdigit(source.at(underscore+2)))
00166     underscore = string::npos;
00167 
00168   string extension = source.substr(dot);
00169   if (extension == ".ma" || extension == ".mb") {
00170     // If we are reading a Maya file (as opposed to a texture image),
00171     // then we always write ".mb" files out (unless -ma was specified
00172     // on the command line).
00173     if (_maya_ascii) {
00174       extension = ".ma";
00175     } else {
00176       extension = ".mb";
00177     }
00178   }
00179 
00180   if (underscore == string::npos) {
00181     // No version number appears to be present.
00182     return source.substr(0, dot) + extension;
00183   } else {
00184     return source.substr(0, underscore) + extension;
00185   }
00186 }
00187 
00188 ////////////////////////////////////////////////////////////////////
00189 //     Function: MayaCopy::copy_maya_file
00190 //       Access: Private
00191 //  Description:
00192 ////////////////////////////////////////////////////////////////////
00193 bool MayaCopy::
00194 copy_maya_file(const Filename &source, const Filename &dest,
00195                CVSSourceDirectory *dir) {
00196   if (!_maya->read(source)) {
00197     maya_cat.error()
00198       << "Unable to read " << source << "\n";
00199     return false;
00200   }
00201 
00202   // Get the set of externally referenced Maya files.
00203   MStringArray refs;
00204   MStatus status = MFileIO::getReferences(refs);
00205   if (status != MStatus::kSuccess) {
00206     status.perror("MItDag constructor");
00207     return false;
00208   }
00209 
00210   // Finally, copy in any referenced Maya files.
00211   unsigned int num_refs = refs.length();
00212 
00213   unsigned int ref_index;
00214   maya_cat.info() << "num_refs = " << num_refs << endl;
00215   for (ref_index = 0; ref_index < num_refs; ref_index++) {
00216     maya_cat.info() << "curr_idx " << _curr_idx << endl;
00217     string lookup = refs[ref_index].asChar();
00218     string blah = "file -q -referenceNode \"" + lookup + "\";";
00219     maya_cat.info() << blah << endl;
00220     MString result;
00221     status = MGlobal::executeCommand(MString(blah.c_str()), result);
00222     maya_cat.info() << "result = " << result.asChar() << endl;
00223 
00224     // for multiple reference of the same model. maya throws in a {#} at the end, ignore that
00225     size_t dup = lookup.find('{');
00226     if (dup != string::npos){
00227       lookup.erase(dup);
00228     }
00229 
00230     // to check out this specific reference is actually loaded or not
00231     // somehow this flag order of MEL script must be observed to guarantee proper working
00232     string refNode = result.asChar();
00233     string refCheckCmd = "file -rfn " + refNode + " -q -dr;";
00234     int deferredRef;
00235     status = MGlobal::executeCommand(MString(refCheckCmd.c_str()), deferredRef);
00236     maya_cat.info() << "deferredRef = " << deferredRef << endl;
00237     if (deferredRef == 1) { // means this reference is deferred, unloaded
00238       continue;
00239     }
00240 
00241     Filename filename = 
00242       _path_replace->convert_path(Filename::from_os_specific(lookup));
00243 
00244     CVSSourceTree::FilePath path =
00245       _tree.choose_directory(filename.get_basename(), dir, _force, _interactive);
00246     Filename new_filename = path.get_rel_from(dir);
00247 
00248     if (maya_cat.is_spam()) {
00249       maya_cat.spam() << "cvs dir " << dir->get_fullpath().to_os_generic() << endl;
00250       maya_cat.spam() << "cvs path " << path.get_fullpath().to_os_generic() << endl;
00251     }
00252     MString result2;
00253 
00254     if (maya_cat.is_debug()) {
00255       string cmdStr = "pwd";
00256       MString result3;
00257       status  = MGlobal::executeCommand(MString(cmdStr.c_str()), result3);
00258       maya_cat.debug() << "result = " << result3.asChar() << "\n";
00259     }
00260     _exec_string.push_back("file -loadReference \"" + string(result.asChar()) + "\" -type \"mayaBinary\" -options \"v=0\" \"" + new_filename.to_os_generic() + "\";");
00261     if (!_omit_ref) {
00262         maya_cat.info() << "executing command: " << _exec_string[_curr_idx] << "\n";
00263         status  = MGlobal::executeCommand(MString(_exec_string[_curr_idx].c_str()));
00264         if (status != MStatus::kSuccess) {
00265           status.perror("loadReference failed");
00266         }
00267      }
00268     _curr_idx++;
00269   }
00270 
00271   if (!_omit_tex) {
00272     // Get all the shaders so we can determine the set of textures.
00273     _shaders.clear();
00274     collect_shaders();
00275     int num_shaders = _shaders.get_num_shaders();
00276     for (int i = 0; i < num_shaders; i++) {
00277       MayaShader *shader = _shaders.get_shader(i);
00278       for (size_t j = 0; j < shader->_all_maps.size(); j++) {
00279         if (!extract_texture(*shader->_all_maps[j], dir)) {
00280           return false;
00281         }
00282       }
00283     }
00284   }
00285 
00286   // Now write out the Maya file.
00287   if (!_maya->write(dest)) {
00288     maya_cat.error()
00289       << "Cannot write " << dest << "\n";
00290     return false;
00291   }
00292   
00293   for (ref_index = 0; ref_index < num_refs; ref_index++) {
00294     if (1) { // we may want an option later to pull in all the referenced files
00295       continue;
00296     }
00297 
00298     string lookup = refs[ref_index].asChar();
00299     // for multiple reference of the same model. maya throws in a {#} at the end, ignore that
00300     size_t dup = lookup.find('{');
00301     if (dup != string::npos){
00302       lookup.erase(dup);
00303     }
00304 
00305     Filename filename = 
00306       _path_replace->convert_path(Filename::from_os_specific(lookup));
00307 
00308     maya_cat.info()
00309       << "External ref: " << filename << "\n";
00310     
00311     // Now import the file
00312     ExtraData ed;
00313     ed._type = FT_maya;
00314     
00315     CVSSourceTree::FilePath path = import(filename, &ed, _model_dir);
00316     if (!path.is_valid()) {
00317       exit(1);
00318     }
00319   }
00320 
00321   return true;
00322 }
00323 
00324 ////////////////////////////////////////////////////////////////////
00325 //     Function: MayaCopy::extract_texture
00326 //       Access: Private
00327 //  Description: Gets the texture out of the indicated color channel
00328 //               and copies it in, updating the channel with the new
00329 //               texture filename.  Returns true on success, false on
00330 //               failure.
00331 ////////////////////////////////////////////////////////////////////
00332 bool MayaCopy::
00333 extract_texture(MayaShaderColorDef &color_def, CVSSourceDirectory *dir) {
00334   Filename texture_filename =
00335     _path_replace->convert_path(color_def._texture_filename);
00336   if (!texture_filename.exists()) {
00337     nout << "*** Error: texture " << texture_filename
00338          << " does not exist.\n";
00339     return false;
00340   } else if (!texture_filename.is_regular_file()) {
00341     nout << "*** Error: texture " << texture_filename
00342          << " is not a regular file.\n";
00343     return false;
00344   } else {
00345     ExtraData ed;
00346     ed._type = FT_texture;
00347     
00348     CVSSourceTree::FilePath texture_path =
00349       import(texture_filename, &ed, _map_dir);
00350     
00351     if (!texture_path.is_valid()) {
00352       return false;
00353     }
00354     
00355     // Update the texture reference to point to the new texture
00356     // filename, relative to the maya file.
00357     Filename new_filename = texture_path.get_rel_from(dir);
00358     color_def.reset_maya_texture(new_filename);
00359   }
00360 
00361   return true;
00362 }
00363 
00364 ////////////////////////////////////////////////////////////////////
00365 //     Function: MayaCopy::copy_texture
00366 //       Access: Private
00367 //  Description:
00368 ////////////////////////////////////////////////////////////////////
00369 bool MayaCopy::
00370 copy_texture(const Filename &source, const Filename &dest,
00371              CVSSourceDirectory *dir) {
00372   if (!copy_binary_file(source, dest)) {
00373     return false;
00374   }
00375 
00376   return true;
00377 }
00378 
00379 ////////////////////////////////////////////////////////////////////
00380 //     Function: MayaCopy::collect_shaders
00381 //       Access: Private
00382 //  Description: Recursively walks through the maya scene graph
00383 //               hierarchy, looking for shaders.
00384 ////////////////////////////////////////////////////////////////////
00385 bool MayaCopy::
00386 collect_shaders() {
00387   MStatus status;
00388 
00389   MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
00390   if (status != MStatus::kSuccess) {
00391     status.perror("MItDag constructor");
00392     return false;
00393   }
00394 
00395   // This while loop walks through the entire Maya hierarchy, one node
00396   // at a time.  Maya's MItDag object automatically performs a
00397   // depth-first traversal of its scene graph.
00398   bool all_ok = true;
00399   while (!dag_iterator.isDone()) {
00400     MDagPath dag_path;
00401     status = dag_iterator.getPath(dag_path);
00402     if (status != MStatus::kSuccess) {
00403       status.perror("MItDag::getPath");
00404     } else {
00405       if (!collect_shader_for_node(dag_path)) {
00406         all_ok = false;
00407       }
00408     }
00409 
00410     dag_iterator.next();
00411   }
00412 
00413   if (!all_ok) {
00414     nout << "Errors encountered in traversal.\n";
00415     return false;
00416   }
00417 
00418   return true;
00419 }
00420 
00421 ////////////////////////////////////////////////////////////////////
00422 //     Function: MayaCopy::collect_shader_for_node
00423 //       Access: Private
00424 //  Description: Gets the relevant shader on the current node, if it
00425 //               has one.
00426 ////////////////////////////////////////////////////////////////////
00427 bool MayaCopy::
00428 collect_shader_for_node(const MDagPath &dag_path) {
00429   MStatus status;
00430   MFnDagNode dag_node(dag_path, &status);
00431   if (status != MStatus::kSuccess) {
00432     status.perror("MFnDagNode constructor");
00433     return false;
00434   }
00435 
00436   if (dag_path.hasFn(MFn::kNurbsSurface)) {
00437     MFnNurbsSurface surface(dag_path, &status);
00438     if (status) {
00439       _shaders.find_shader_for_node(surface.object(), false);
00440     }
00441 
00442   } else if (dag_path.hasFn(MFn::kMesh)) {
00443     MFnMesh mesh(dag_path, &status);
00444     if (status) {
00445       // Meshes may have multiple different shaders.
00446       MObjectArray shaders;
00447       MIntArray poly_shader_indices;
00448 
00449       status = mesh.getConnectedShaders(dag_path.instanceNumber(),
00450                                         shaders, poly_shader_indices);
00451       if (status) {
00452         unsigned int num_shaders = shaders.length();
00453         for (unsigned int shader_index = 0;
00454              shader_index < num_shaders; 
00455              shader_index++) {
00456           MObject engine = shaders[shader_index];
00457           _shaders.find_shader_for_shading_engine(engine, false);
00458         }
00459       }
00460     }
00461 
00462   } else {
00463     // Ignoring other kinds of node.
00464   }
00465 
00466   return true;
00467 }
00468 
00469 
00470 int main(int argc, char *argv[]) {
00471   // We don't want pystub on linux, since it gives problems with Maya's python.
00472 #ifdef _WIN32
00473   // A call to pystub() to force libpystub.so to be linked in.
00474   pystub();
00475 #endif
00476 
00477   MayaCopy prog;
00478   prog.parse_command_line(argc, argv);
00479   prog.run();
00480   return 0;
00481 }
00482 
 All Classes Functions Variables Enumerations