Panda3D
mayaCopy.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file mayaCopy.cxx
10  * @author drose
11  * @date 2002-05-10
12  * Modified 19Mar10 by ETC PandaSE team (see
13  * header comment for mayaToEgg.cxx for more details)
14  */
15 
16 #include "mayaCopy.h"
17 #include "config_maya.h"
18 #include "cvsSourceDirectory.h"
19 #include "dcast.h"
20 
21 #include "pre_maya_include.h"
22 #include <maya/MStringArray.h>
23 #include <maya/MGlobal.h>
24 #include <maya/MFileIO.h>
25 #include <maya/MItDag.h>
26 #include <maya/MFnDagNode.h>
27 #include <maya/MFnNurbsSurface.h>
28 #include <maya/MFnMesh.h>
29 #include <maya/MObject.h>
30 #include <maya/MDagPath.h>
31 #include <maya/MIntArray.h>
32 #include "post_maya_include.h"
33 
34 #include "mayaShader.h"
35 
36 using std::endl;
37 using std::string;
38 
39 /**
40  *
41  */
42 MayaCopy::
43 MayaCopy() {
44  set_program_brief("copy Maya .mb files into a CVS source hierarchy");
45  set_program_description
46  ("mayacopy copies one or more Maya .mb files into a "
47  "CVS source hierarchy. "
48  "Rather than copying the named files immediately into the current "
49  "directory, it first scans the entire source hierarchy, identifying all "
50  "the already-existing files. If the named file to copy matches the "
51  "name of an already-existing file in the current directory or elsewhere "
52  "in the hierarchy, that file is overwritten. Other .mb files, as "
53  "well as texture files, that are externally referenced by the "
54  "named .mb file(s) are similarly copied.");
55 
56  clear_runlines();
57  add_runline("[opts] file.mb [file.mb ... ]");
58 
59  add_option
60  ("keepver", "", 0,
61  "Don't attempt to strip the Maya version number from the tail of the "
62  "source filename before it is copied into the tree.",
63  &CVSCopy::dispatch_none, &_keep_ver);
64 
65  /*
66  add_option
67  ("rp", "replace_prefix", 80,
68  "use these prefixes when replacing reference with the recently copied file from the "
69  "source filename before it is copied into the tree.",
70  &CVSCopy::dispatch_vector_string, NULL, &_replace_prefix);
71  */
72 
73  add_option
74  ("omittex", "", 0,
75  "Character animation files do not need to copy the texures. "
76  "This option omits the textures of the models to be re-mayacopied",
77  &CVSCopy::dispatch_none, &_omit_tex);
78 
79  add_option
80  ("omitref", "", 0,
81  "Character animation files do not need to copy internal file references. "
82  "This option omits the references of the models to be re-mayacopied",
83  &CVSCopy::dispatch_none, &_omit_ref);
84 
85  add_option
86  ("ma", "", 0,
87  "Write a .ma file instead of a .mb file (regardless of input type)",
88  &CVSCopy::dispatch_none, &_maya_ascii);
89 
90  add_path_replace_options();
91 }
92 
93 /**
94  *
95  */
96 void MayaCopy::
97 run() {
98  _maya = MayaApi::open_api(_program_name);
99  if (!_maya->is_valid()) {
100  nout << "Unable to initialize Maya.\n";
101  exit(1);
102  }
103 
104  SourceFiles::iterator fi;
105  for (fi = _source_files.begin(); fi != _source_files.end(); ++fi) {
106  _curr_idx = 0;
107  ExtraData ed;
108  ed._type = FT_maya;
109 
110  CVSSourceTree::FilePath path = import(*fi, &ed, _model_dir);
111  if (!path.is_valid()) {
112  nout << "\nUnable to copy, aborting!\n\n";
113  exit(1);
114  }
115  }
116 }
117 
118 /**
119  * Called by import() if verify_file() indicates that a file needs to be
120  * copied. This does the actual copy of a file from source to destination.
121  * If new_file is true, then dest does not already exist.
122  */
123 bool MayaCopy::
124 copy_file(const Filename &source, const Filename &dest,
125  CVSSourceDirectory *dir, void *extra_data, bool new_file) {
126  ExtraData *ed = (ExtraData *)extra_data;
127  switch (ed->_type) {
128  case FT_maya:
129  return copy_maya_file(source, dest, dir);
130 
131  case FT_texture:
132  if (_omit_tex) {
133  return true;
134  }
135  return copy_texture(source, dest, dir);
136  }
137 
138  nout << "Internal error: invalid type " << (int)ed->_type << "\n";
139  return false;
140 }
141 
142 /**
143  * Given a source filename (including the basename only, without a dirname),
144  * return the appropriate corresponding filename within the source directory.
145  * This may be used by derived classes to, for instance, strip a version
146  * number from the filename.
147  */
148 string MayaCopy::
149 filter_filename(const string &source) {
150  if (_keep_ver) {
151  return source;
152  }
153 
154  size_t dot = source.rfind('.');
155  size_t underscore = source.rfind("_v", dot);
156  if ((underscore != string::npos) && !isdigit(source.at(underscore+2)))
157  underscore = string::npos;
158 
159  string extension = source.substr(dot);
160  if (extension == ".ma" || extension == ".mb") {
161  // If we are reading a Maya file (as opposed to a texture image), then we
162  // always write ".mb" files out (unless -ma was specified on the command
163  // line).
164  if (_maya_ascii) {
165  extension = ".ma";
166  } else {
167  extension = ".mb";
168  }
169  }
170 
171  if (underscore == string::npos) {
172  // No version number appears to be present.
173  return source.substr(0, dot) + extension;
174  } else {
175  return source.substr(0, underscore) + extension;
176  }
177 }
178 
179 /**
180  *
181  */
182 bool MayaCopy::
183 copy_maya_file(const Filename &source, const Filename &dest,
184  CVSSourceDirectory *dir) {
185  if (!_maya->read(source)) {
186  maya_cat.error()
187  << "Unable to read " << source << "\n";
188  return false;
189  }
190 
191  // Get the set of externally referenced Maya files.
192  MStringArray refs;
193  MStatus status = MFileIO::getReferences(refs);
194  if (status != MStatus::kSuccess) {
195  status.perror("MItDag constructor");
196  return false;
197  }
198 
199  // Finally, copy in any referenced Maya files.
200  unsigned int num_refs = refs.length();
201 
202  unsigned int ref_index;
203  maya_cat.info() << "num_refs = " << num_refs << endl;
204  for (ref_index = 0; ref_index < num_refs; ref_index++) {
205  maya_cat.info() << "curr_idx " << _curr_idx << endl;
206  string lookup = refs[ref_index].asChar();
207  string blah = "file -q -referenceNode \"" + lookup + "\";";
208  maya_cat.info() << blah << endl;
209  MString result;
210  status = MGlobal::executeCommand(MString(blah.c_str()), result);
211  maya_cat.info() << "result = " << result.asChar() << endl;
212 
213  // for multiple reference of the same model. maya throws in a {#} at the
214  // end, ignore that
215  size_t dup = lookup.find('{');
216  if (dup != string::npos){
217  lookup.erase(dup);
218  }
219 
220  // to check out this specific reference is actually loaded or not somehow
221  // this flag order of MEL script must be observed to guarantee proper
222  // working
223  string refNode = result.asChar();
224  string refCheckCmd = "file -rfn " + refNode + " -q -dr;";
225  int deferredRef;
226  status = MGlobal::executeCommand(MString(refCheckCmd.c_str()), deferredRef);
227  maya_cat.info() << "deferredRef = " << deferredRef << endl;
228  if (deferredRef == 1) { // means this reference is deferred, unloaded
229  continue;
230  }
231 
232  Filename filename =
233  _path_replace->convert_path(Filename::from_os_specific(lookup));
234 
236  _tree.choose_directory(filename.get_basename(), dir, _force, _interactive);
237  Filename new_filename = path.get_rel_from(dir);
238 
239  if (maya_cat.is_spam()) {
240  maya_cat.spam() << "cvs dir " << dir->get_fullpath().to_os_generic() << endl;
241  maya_cat.spam() << "cvs path " << path.get_fullpath().to_os_generic() << endl;
242  }
243  MString result2;
244 
245  if (maya_cat.is_debug()) {
246  string cmdStr = "pwd";
247  MString result3;
248  status = MGlobal::executeCommand(MString(cmdStr.c_str()), result3);
249  maya_cat.debug() << "result = " << result3.asChar() << "\n";
250  }
251  _exec_string.push_back("file -loadReference \"" + string(result.asChar()) + "\" -type \"mayaBinary\" -options \"v=0\" \"" + new_filename.to_os_generic() + "\";");
252  if (!_omit_ref) {
253  maya_cat.info() << "executing command: " << _exec_string[_curr_idx] << "\n";
254  status = MGlobal::executeCommand(MString(_exec_string[_curr_idx].c_str()));
255  if (status != MStatus::kSuccess) {
256  status.perror("loadReference failed");
257  }
258  }
259  _curr_idx++;
260  }
261 
262  if (!_omit_tex) {
263  // Get all the shaders so we can determine the set of textures.
264  _shaders.clear();
265  collect_shaders();
266  int num_shaders = _shaders.get_num_shaders();
267  for (int i = 0; i < num_shaders; i++) {
268  MayaShader *shader = _shaders.get_shader(i);
269  for (size_t j = 0; j < shader->_all_maps.size(); j++) {
270  if (!extract_texture(*shader->_all_maps[j], dir)) {
271  return false;
272  }
273  }
274  }
275  }
276 
277  // Now write out the Maya file.
278  if (!_maya->write(dest)) {
279  maya_cat.error()
280  << "Cannot write " << dest << "\n";
281  return false;
282  }
283 
284  for (ref_index = 0; ref_index < num_refs; ref_index++) {
285  if (1) { // we may want an option later to pull in all the referenced files
286  continue;
287  }
288 
289  string lookup = refs[ref_index].asChar();
290  // for multiple reference of the same model. maya throws in a {#} at the
291  // end, ignore that
292  size_t dup = lookup.find('{');
293  if (dup != string::npos){
294  lookup.erase(dup);
295  }
296 
297  Filename filename =
298  _path_replace->convert_path(Filename::from_os_specific(lookup));
299 
300  maya_cat.info()
301  << "External ref: " << filename << "\n";
302 
303  // Now import the file
304  ExtraData ed;
305  ed._type = FT_maya;
306 
307  CVSSourceTree::FilePath path = import(filename, &ed, _model_dir);
308  if (!path.is_valid()) {
309  exit(1);
310  }
311  }
312 
313  return true;
314 }
315 
316 /**
317  * Gets the texture out of the indicated color channel and copies it in,
318  * updating the channel with the new texture filename. Returns true on
319  * success, false on failure.
320  */
321 bool MayaCopy::
322 extract_texture(MayaShaderColorDef &color_def, CVSSourceDirectory *dir) {
323  Filename texture_filename =
324  _path_replace->convert_path(color_def._texture_filename);
325  if (!texture_filename.exists()) {
326  nout << "*** Error: texture " << texture_filename
327  << " does not exist.\n";
328  return false;
329  } else if (!texture_filename.is_regular_file()) {
330  nout << "*** Error: texture " << texture_filename
331  << " is not a regular file.\n";
332  return false;
333  } else {
334  ExtraData ed;
335  ed._type = FT_texture;
336 
337  CVSSourceTree::FilePath texture_path =
338  import(texture_filename, &ed, _map_dir);
339 
340  if (!texture_path.is_valid()) {
341  return false;
342  }
343 
344  // Update the texture reference to point to the new texture filename,
345  // relative to the maya file.
346  Filename new_filename = texture_path.get_rel_from(dir);
347  color_def.reset_maya_texture(new_filename);
348  }
349 
350  return true;
351 }
352 
353 /**
354  *
355  */
356 bool MayaCopy::
357 copy_texture(const Filename &source, const Filename &dest,
358  CVSSourceDirectory *dir) {
359  if (!copy_binary_file(source, dest)) {
360  return false;
361  }
362 
363  return true;
364 }
365 
366 /**
367  * Recursively walks through the maya scene graph hierarchy, looking for
368  * shaders.
369  */
370 bool MayaCopy::
371 collect_shaders() {
372  MStatus status;
373 
374  MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
375  if (status != MStatus::kSuccess) {
376  status.perror("MItDag constructor");
377  return false;
378  }
379 
380  // This while loop walks through the entire Maya hierarchy, one node at a
381  // time. Maya's MItDag object automatically performs a depth-first
382  // traversal of its scene graph.
383  bool all_ok = true;
384  while (!dag_iterator.isDone()) {
385  MDagPath dag_path;
386  status = dag_iterator.getPath(dag_path);
387  if (status != MStatus::kSuccess) {
388  status.perror("MItDag::getPath");
389  } else {
390  if (!collect_shader_for_node(dag_path)) {
391  all_ok = false;
392  }
393  }
394 
395  dag_iterator.next();
396  }
397 
398  if (!all_ok) {
399  nout << "Errors encountered in traversal.\n";
400  return false;
401  }
402 
403  return true;
404 }
405 
406 /**
407  * Gets the relevant shader on the current node, if it has one.
408  */
409 bool MayaCopy::
410 collect_shader_for_node(const MDagPath &dag_path) {
411  MStatus status;
412  MFnDagNode dag_node(dag_path, &status);
413  if (status != MStatus::kSuccess) {
414  status.perror("MFnDagNode constructor");
415  return false;
416  }
417 
418  if (dag_path.hasFn(MFn::kNurbsSurface)) {
419  MFnNurbsSurface surface(dag_path, &status);
420  if (status) {
421  _shaders.find_shader_for_node(surface.object(), false);
422  }
423 
424  } else if (dag_path.hasFn(MFn::kMesh)) {
425  MFnMesh mesh(dag_path, &status);
426  if (status) {
427  // Meshes may have multiple different shaders.
428  MObjectArray shaders;
429  MIntArray poly_shader_indices;
430 
431  status = mesh.getConnectedShaders(dag_path.instanceNumber(),
432  shaders, poly_shader_indices);
433  if (status) {
434  unsigned int num_shaders = shaders.length();
435  for (unsigned int shader_index = 0;
436  shader_index < num_shaders;
437  shader_index++) {
438  MObject engine = shaders[shader_index];
439  _shaders.find_shader_for_shading_engine(engine, false);
440  }
441  }
442  }
443 
444  } else {
445  // Ignoring other kinds of node.
446  }
447 
448  return true;
449 }
450 
451 
452 int main(int argc, char *argv[]) {
453  MayaCopy prog;
454  prog.parse_command_line(argc, argv);
455  prog.run();
456  return 0;
457 }
Filename::is_regular_file
bool is_regular_file() const
Returns true if the filename exists and is the name of a regular file (i.e.
Definition: filename.cxx:1297
CVSSourceTree::FilePath::get_fullpath
Filename get_fullpath() const
Returns the full path to this file.
Definition: cvsSourceTree.cxx:533
MayaShaders::clear
void clear()
Frees all of the previously-defined MayaShader objects associated with this set.
Definition: mayaShaders.cxx:189
MayaShaders::find_shader_for_node
MayaShader * find_shader_for_node(MObject node, bool legacy_shader)
Extracts the shader assigned to the indicated node.
Definition: mayaShaders.cxx:51
Filename::from_os_specific
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
Filename::exists
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaShaderColorDef
This defines the various attributes that Maya may associate with the "color" channel for a particular...
Definition: mayaShaderColorDef.h:34
post_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaShader
Corresponds to a single "shader" in Maya.
Definition: mayaShader.h:30
mayaShader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cvsSourceDirectory.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
mayaCopy.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename::to_os_generic
std::string to_os_generic() const
This is similar to to_os_specific(), but it is designed to generate a filename that can be understood...
Definition: filename.cxx:1182
CVSSourceDirectory::get_fullpath
Filename get_fullpath() const
Returns the full pathname to this particular directory.
Definition: cvsSourceDirectory.cxx:62
ProgramBase::parse_command_line
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
Definition: programBase.cxx:274
config_maya.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaCopy
A program to copy Maya .mb files into the cvs tree.
Definition: mayaCopy.h:37
pre_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CVSSourceDirectory
This represents one particular directory in the hierarchy of source directory files.
Definition: cvsSourceDirectory.h:35
MayaShaders::find_shader_for_shading_engine
MayaShader * find_shader_for_shading_engine(MObject engine, bool legacy_shader)
Returns the MayaShader object associated with the indicated "shading engine".
Definition: mayaShaders.cxx:133
MayaShaderColorDef::reset_maya_texture
bool reset_maya_texture(const Filename &texture)
Changes the texture filename stored in the Maya file for this particular shader.
Definition: mayaShaderColorDef.cxx:210
MayaShaders::get_num_shaders
int get_num_shaders() const
Returns the number of unique MayaShaders that have been discovered so far.
Definition: mayaShaders.cxx:171
CVSSourceTree::FilePath
Definition: cvsSourceTree.h:52
MayaShaders::get_shader
MayaShader * get_shader(int n) const
Returns the nth MayaShader that has been discovered so far.
Definition: mayaShaders.cxx:179
Filename::get_basename
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
CVSSourceTree::FilePath::is_valid
bool is_valid() const
Returns true if this FilePath represents a valid file, or false if it represents an error return.
Definition: cvsSourceTree.cxx:516
CVSSourceTree::FilePath::get_rel_from
Filename get_rel_from(const CVSSourceDirectory *other) const
Returns the relative path to this file as seen from the indicated source directory.
Definition: cvsSourceTree.cxx:543