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 }
bool is_valid() const
Returns true if this FilePath represents a valid file, or false if it represents an error return.
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_...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaShader * find_shader_for_node(MObject node, bool legacy_shader)
Extracts the shader assigned to the indicated node.
Definition: mayaShaders.cxx:51
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename get_fullpath() const
Returns the full pathname to this particular directory.
This represents one particular directory in the hierarchy of source directory files.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
This defines the various attributes that Maya may associate with the "color" channel for a particular...
MayaShader * get_shader(int n) const
Returns the nth MayaShader that has been discovered so far.
Corresponds to a single "shader" in Maya.
Definition: mayaShader.h:30
MayaShader * find_shader_for_shading_engine(MObject engine, bool legacy_shader)
Returns the MayaShader object associated with the indicated "shading engine".
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
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename get_fullpath() const
Returns the full path to this file.
void clear()
Frees all of the previously-defined MayaShader objects associated with this set.
bool reset_maya_texture(const Filename &texture)
Changes the texture filename stored in the Maya file for this particular shader.
Filename get_rel_from(const CVSSourceDirectory *other) const
Returns the relative path to this file as seen from the indicated source directory.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A program to copy Maya .mb files into the cvs tree.
Definition: mayaCopy.h:37
int get_num_shaders() const
Returns the number of unique MayaShaders that have been discovered so far.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.