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  */
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  */
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  */
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  */
349 get_units() {
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  */
377 set_units(DistanceUnit unit) {
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 }
get_binary_name
Returns the name of the binary executable that started this program, if it can be determined.
get_cwd
Returns the name of the current working directory.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
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
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
This class presents a wrapper around the global Maya interface.
Definition: mayaApi.h:30
void set_units(DistanceUnit unit)
Set Maya's UI units.
Definition: mayaApi.cxx:377
CoordinateSystem get_coordinate_system()
Returns Maya's internal coordinate system in effect.
Definition: mayaApi.cxx:413
bool clear()
Resets the global model space to the empty state, for instance in preparation for building a new file...
Definition: mayaApi.cxx:336
DistanceUnit get_units()
Returns Maya's internal units in effect.
Definition: mayaApi.cxx:349
bool write(const Filename &filename)
Writes the global model space to the indicated file.
Definition: mayaApi.cxx:292
bool read(const Filename &filename)
Reads the indicated maya file into the global model space.
Definition: mayaApi.cxx:249
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
Definition: thread.I:192
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
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.