Panda3D
|
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