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