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
235 CVSSourceTree::FilePath path =
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 }
CVSSourceDirectory
This represents one particular directory in the hierarchy of source directory files.
Definition: cvsSourceDirectory.h:35
CVSSourceDirectory::get_fullpath
Filename get_fullpath() const
Returns the full pathname to this particular directory.
Definition: cvsSourceDirectory.cxx:62
CVSSourceTree::FilePath
Definition: cvsSourceTree.h:52
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
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_fullpath
Filename get_fullpath() const
Returns the full path to this file.
Definition: cvsSourceTree.cxx:533
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
Filename::get_basename
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
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::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
Filename::exists
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
MayaCopy
A program to copy Maya .mb files into the cvs tree.
Definition: mayaCopy.h:37
MayaShaderColorDef
This defines the various attributes that Maya may associate with the "color" channel for a particular...
Definition: mayaShaderColorDef.h:34
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
MayaShader
Corresponds to a single "shader" in Maya.
Definition: mayaShader.h:30
MayaShaders::clear
void clear()
Frees all of the previously-defined MayaShader objects associated with this set.
Definition: mayaShaders.cxx:189
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
MayaShaders::get_shader
MayaShader * get_shader(int n) const
Returns the nth MayaShader that has been discovered so far.
Definition: mayaShaders.cxx:179
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
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
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:286
config_maya.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cvsSourceDirectory.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
mayaCopy.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
mayaShader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
post_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pre_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.