Panda3D
mayaApi.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 mayaApi.cxx
10  * @author drose
11  * @date 2002-04-15
12  */
13 
14 #include "mayaApi.h"
15 #include "config_maya.h"
16 #include "string_utils.h"
17 #include "thread.h"
18 
19 #include "pre_maya_include.h"
20 #include <maya/MGlobal.h>
21 #include <maya/MDistance.h>
22 #include <maya/MFileIO.h>
23 #include <maya/MLibrary.h>
24 #include <maya/MStatus.h>
25 #include <maya/MFnAnimCurve.h>
26 #include "post_maya_include.h"
27 
28 #ifdef WIN32_VC
29 #include <direct.h> // for chdir()
30 #endif
31 
32 using std::string;
33 
34 MayaApi *MayaApi::_global_api = nullptr;
35 
36 // We need this bogus object just to force the application to link with
37 // OpenMayaAnim.lib; otherwise, Maya will complain (when compiled on Windows)
38 // that it is unable to find source plug 'ikRPsolver.msg'.
39 static MFnAnimCurve force_link_with_OpenMayaAnim;
40 
41 /**
42  * Don't attempt to create this object directly; instead, use the open_api()
43  * method.
44  */
45 MayaApi::
46 MayaApi(const string &program_name, bool view_license, bool revert_dir) {
47  if (program_name == "plug-in") {
48  // In this special case, we are invoking the code from within a plug-in,
49  // so we need not (and should not) call MLibrary::initialize().
50  _plug_in = true;
51  _is_valid = true;
52  return;
53  }
54 
55  // Otherwise, if program_name is any other name, we are invoking the code
56  // from a standalone application and we do need to call
57  // MLibrary::initialize().
58  _plug_in = false;
59 
60  // Beginning with Maya4.5, the call to initialize seems to change the
61  // current directory! Yikes!
62 
63  // Furthermore, the current directory may change during the call to any Maya
64  // function! Egad!
66  MStatus stat = MLibrary::initialize(false, (char *)program_name.c_str(), view_license);
67 
68  int error_count = init_maya_repeat_count;
69  while (!stat && error_count > 1) {
70  stat.perror("MLibrary::initialize");
71  Thread::sleep(init_maya_timeout);
72  stat = MLibrary::initialize(false, (char *)program_name.c_str(), view_license);
73  --error_count;
74  }
75 
76  // Restore the current directory. Ever since Maya 2010, there seems to be
77  // some bad mojo when you do this.
78  if( revert_dir ){
79  string dirname = _cwd.to_os_specific();
80  if (chdir(dirname.c_str()) < 0) {
81  maya_cat.warning()
82  << "Unable to restore current directory to " << _cwd
83  << " after initializing Maya.\n";
84  } else {
85  if (maya_cat.is_debug()) {
86  maya_cat.debug()
87  << "Restored current directory to " << _cwd << "\n";
88  }
89  }
90  }
91 
92 
93  if (!stat) {
94  stat.perror("MLibrary::initialize");
95  _is_valid = false;
96  } else {
97  _is_valid = true;
98  }
99 }
100 
101 /**
102  * Don't attempt to copy MayaApi objects. There should be only one of these
103  * in the world at a time.
104  */
105 MayaApi::
106 MayaApi(const MayaApi &copy) {
107  nassertv(false);
108 }
109 
110 /**
111  * Don't attempt to copy MayaApi objects. There should be only one of these
112  * in the world at a time.
113  */
114 void MayaApi::
115 operator = (const MayaApi &copy) {
116  nassertv(false);
117 }
118 
119 /**
120  *
121  */
122 MayaApi::
123 ~MayaApi() {
124  nassertv(_global_api == this);
125  if (_is_valid && !_plug_in) {
126  // Caution! Calling this function seems to call exit() somewhere within
127  // Maya code.
128  MLibrary::cleanup();
129  }
130  _global_api = nullptr;
131 }
132 
133 /**
134  * Opens the Maya API, if it is not already open, and returns a pointer
135  * representing this connection. When you are done using the Maya API, let
136  * the pointer destruct.
137  *
138  * If program_name is supplied, it is passed to Maya as the name of the
139  * currently-executing program. Otherwise, the current program name is
140  * extracted from the execution environment, if possible. The special
141  * program_name "plug-in" is used for code that is intended to be invoked as a
142  * plug-in only; in this case, the maya library is not re-initialized.
143  */
144 PT(MayaApi) MayaApi::
145 open_api(string program_name, bool view_license, bool revertdir) {
146  if (_global_api == nullptr) {
147  // We need to create a new MayaApi object.
148  if (program_name.empty()) {
149  program_name = ExecutionEnvironment::get_binary_name();
150  if (program_name.empty()) {
151  program_name = "Panda";
152  }
153  }
154 
155  _global_api = new MayaApi(program_name, view_license, revertdir);
156 
157  // Try to compare the string-formatted runtime version number with the
158  // numeric compile-time version number, so we can sanity check our runtime
159  // environment. (Sure would be nice if Maya provided an apples-to-apples
160  // comparison for us.)
161 
162  // According to the Maya specs, the numeric value is derived by taking the
163  // Maya version number and deleting the '.' characters, while also
164  // ignoring everything after the second dot (and, for some reason,
165  // appending a 0).
166 
167  string runtime_version = MGlobal::mayaVersion().asChar();
168  string simple_runtime_version = runtime_version;
169  runtime_version = trim(runtime_version);
170 
171  // If the version number contains a space, stop there (that would be
172  // "service pack 1" or whatever).
173  size_t space = runtime_version.find(' ');
174  if (space != string::npos) {
175  runtime_version = runtime_version.substr(0, space);
176  }
177 
178  int rtver_a, rtver_b;
179  size_t dot1 = runtime_version.find('.');
180  if (dot1 == string::npos) {
181  string_to_int(runtime_version, rtver_a);
182  rtver_b = 0;
183 
184  } else {
185  string_to_int(runtime_version.substr(0, dot1), rtver_a);
186 
187  size_t dot2 = runtime_version.find('.', dot1 + 1);
188  if (dot2 == string::npos) {
189  string_to_int(runtime_version.substr(dot1 + 1), rtver_b);
190 
191  } else {
192  string_to_int(runtime_version.substr(dot1 + 1, dot2 - dot1 - 1), rtver_b);
193  simple_runtime_version = runtime_version.substr(0, dot2);
194  }
195  }
196 
197  int runtime_version_int = rtver_a * 100 + rtver_b * 10;
198 
199  if (maya_cat.is_debug()) {
200  maya_cat.debug()
201  << "Compiled with Maya library version "
202  << (MAYA_API_VERSION / 100) << "." << (MAYA_API_VERSION / 10) % 10
203  << " (" << MAYA_API_VERSION << "); running with library version "
204  << runtime_version << ".\n";
205  }
206 
207  if (MAYA_API_VERSION / 10 != runtime_version_int / 10) {
208  maya_cat.warning()
209  << "This program was compiled using Maya version "
210  << (MAYA_API_VERSION / 100) << "." << (MAYA_API_VERSION / 10) % 10
211  << ", but you are now running it with Maya version "
212  << simple_runtime_version
213  << ". The program may crash or produce incorrect results.\n\n";
214  }
215  }
216 
217  return _global_api;
218 }
219 
220 /**
221  * Returns true if the API has been successfully opened and may be used, or
222  * false if there is some problem.
223  */
224 bool MayaApi::
225 is_valid() const {
226  return _is_valid;
227 }
228 
229 #ifdef WIN32
230 static string
231 back_to_front_slash(const string &str) {
232  string result = str;
233  string::iterator si;
234  for (si = result.begin(); si != result.end(); ++si) {
235  if ((*si) == '\\') {
236  (*si) = '/';
237  }
238  }
239 
240  return result;
241 }
242 #endif // WIN32
243 
244 /**
245  * Reads the indicated maya file into the global model space. Returns true if
246  * successful, false otherwise.
247  */
248 bool MayaApi::
249 read(const Filename &filename) {
250  MFileIO::newFile(true);
251 
252  maya_cat.info() << "Reading " << filename << "\n";
253 
254  // Load the file into Maya. Maya seems to want forward slashes, even on
255  // Windows.
256  string os_filename = filename.to_os_generic();
257 
258  string dirname = _cwd.to_os_specific();
259  if (maya_cat.is_debug()) {
260  maya_cat.debug() << "cwd(read:before): " << dirname.c_str() << std::endl;
261  }
262 
263  MFileIO::newFile(true);
264  MStatus stat = MFileIO::open(os_filename.c_str());
265  // Beginning with Maya2008, the call to read seem to change the current
266  // directory specially if there is a refrence file! Yikes!
267 
268  // Furthermore, the current directory may change during the call to any Maya
269  // function! Egad!
270  if (chdir(dirname.c_str()) < 0) {
271  maya_cat.warning()
272  << "Unable to restore current directory after ::read to " << _cwd
273  << " after initializing Maya.\n";
274  } else {
275  if (maya_cat.is_debug()) {
276  maya_cat.debug()
277  << "Restored current directory after ::read to " << _cwd << "\n";
278  }
279  }
280  if (!stat) {
281  stat.perror(os_filename.c_str());
282  return false;
283  }
284  return true;
285 }
286 
287 /**
288  * Writes the global model space to the indicated file. Returns true if
289  * successful, false otherwise.
290  */
291 bool MayaApi::
292 write(const Filename &filename) {
293  maya_cat.info() << "Writing " << filename << "\n";
294  string os_filename = filename.to_os_generic();
295 
296  string dirname = _cwd.to_os_specific();
297  if (maya_cat.is_debug()) {
298  maya_cat.debug() << "cwd(write:before): " << dirname.c_str() << std::endl;
299  }
300 
301  const char *type = "mayaBinary";
302  string extension = filename.get_extension();
303  if (extension == "ma") {
304  type = "mayaAscii";
305  }
306 
307  MStatus stat = MFileIO::saveAs(os_filename.c_str(), type, true);
308  if (!stat) {
309  stat.perror(os_filename.c_str());
310  return false;
311  }
312  // Beginning with Maya2008, the call to read seem to change the current
313  // directory specially if there is a refrence file! Yikes!
314 
315  // Furthermore, the current directory may change during the call to any Maya
316  // function! Egad!
317  if (chdir(dirname.c_str()) < 0) {
318  maya_cat.warning()
319  << "Unable to restore current directory after ::write to " << _cwd
320  << " after initializing Maya.\n";
321  } else {
322  if (maya_cat.is_debug()) {
323  maya_cat.debug()
324  << "Restored current directory after ::write to " << _cwd << "\n";
325  }
326  }
327  return true;
328 }
329 
330 /**
331  * Resets the global model space to the empty state, for instance in
332  * preparation for building a new file. Returns true if successful, false
333  * otherwise.
334  */
335 bool MayaApi::
336 clear() {
337  MStatus stat = MFileIO::newFile(true);
338  if (!stat) {
339  stat.perror("clear");
340  return false;
341  }
342  return true;
343 }
344 
345 /**
346  * Returns Maya's internal units in effect.
347  */
350  switch (MDistance::internalUnit()) {
351  case MDistance::kInches:
352  return DU_inches;
353  case MDistance::kFeet:
354  return DU_feet;
355  case MDistance::kYards:
356  return DU_yards;
357  case MDistance::kMiles:
358  return DU_statute_miles;
359  case MDistance::kMillimeters:
360  return DU_millimeters;
361  case MDistance::kCentimeters:
362  return DU_centimeters;
363  case MDistance::kKilometers:
364  return DU_kilometers;
365  case MDistance::kMeters:
366  return DU_meters;
367 
368  default:
369  return DU_invalid;
370  }
371 }
372 
373 /**
374  * Set Maya's UI units.
375  */
376 void MayaApi::
378  switch (unit) {
379  case DU_inches:
380  MDistance::setUIUnit(MDistance::kInches);
381  break;
382  case DU_feet:
383  MDistance::setUIUnit(MDistance::kFeet);
384  break;
385  case DU_yards:
386  MDistance::setUIUnit(MDistance::kYards);
387  break;
388  case DU_statute_miles:
389  MDistance::setUIUnit(MDistance::kMiles);
390  break;
391  case DU_millimeters:
392  MDistance::setUIUnit(MDistance::kMillimeters);
393  break;
394  case DU_centimeters:
395  MDistance::setUIUnit(MDistance::kCentimeters);
396  break;
397  case DU_kilometers:
398  MDistance::setUIUnit(MDistance::kKilometers);
399  break;
400  case DU_meters:
401  MDistance::setUIUnit(MDistance::kMeters);
402  break;
403 
404  default:
405  ;
406  }
407 }
408 
409 /**
410  * Returns Maya's internal coordinate system in effect.
411  */
412 CoordinateSystem MayaApi::
414  if (MGlobal::isYAxisUp()) {
415  return CS_yup_right;
416  } else {
417  return CS_zup_right;
418  }
419 }
int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
void set_units(DistanceUnit unit)
Set Maya's UI units.
Definition: mayaApi.cxx:377
bool read(const Filename &filename)
Reads the indicated maya file into the global model space.
Definition: mayaApi.cxx:249
get_cwd
Returns the name of the current working directory.
bool write(const Filename &filename)
Writes the global model space to the indicated file.
Definition: mayaApi.cxx:292
DistanceUnit get_units()
Returns Maya's internal units in effect.
Definition: mayaApi.cxx:349
bool clear()
Resets the global model space to the empty state, for instance in preparation for building a new file...
Definition: mayaApi.cxx:336
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PT(MayaApi) MayaApi
Opens the Maya API, if it is not already open, and returns a pointer representing this connection.
Definition: mayaApi.cxx:144
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DistanceUnit
This enumerated type lists all the kinds of units we're likely to come across in model conversion pro...
Definition: distanceUnit.h:23
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
Definition: thread.I:192
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CoordinateSystem get_coordinate_system()
Returns Maya's internal coordinate system in effect.
Definition: mayaApi.cxx:413
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class presents a wrapper around the global Maya interface.
Definition: mayaApi.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.