Panda3D
mayaToEgg_server.cxx
1 // Filename: mayaToEgg_server.cxx
2 // Adapted by: cbrunner (09Nov09)
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 #if defined(WIN32_VC) || defined(WIN64_VC)
16 #include <direct.h> // for chdir
17 #endif
18 #include "mayaToEgg_server.h"
19 #include "mayaToEggConverter.h"
20 #include "config_mayaegg.h"
21 #include "config_maya.h" // for maya_cat
22 #include "globPattern.h"
23 #ifdef _WIN32
24  #include "pystub.h"
25 #endif
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: MayaToEggServer::Constructor
29 // Access: Public
30 // Description:
31 ////////////////////////////////////////////////////////////////////
32 MayaToEggServer::
33 MayaToEggServer() :
34  SomethingToEgg("Maya", ".mb")
35 {
36  add_path_replace_options();
37  add_path_store_options();
42 
43  set_program_brief("convert Maya model files to .egg");
44  set_program_description
45  ("This program converts Maya model files to egg. Static and animatable "
46  "models can be converted, with polygon or NURBS output. Animation tables "
47  "can also be generated to apply to an animatable model.");
48 
49  add_option
50  ("p", "", 0,
51  "Generate polygon output only. Tesselate all NURBS surfaces to "
52  "polygons via the built-in Maya tesselator. The tesselation will "
53  "be based on the tolerance factor given by -ptol.",
54  &MayaToEggServer::dispatch_none, &_polygon_output);
55 
56  add_option
57  ("ptol", "tolerance", 0,
58  "Specify the fit tolerance for Maya polygon tesselation. The smaller "
59  "the number, the more polygons will be generated. The default is "
60  "0.01.",
61  &MayaToEggServer::dispatch_double, NULL, &_polygon_tolerance);
62 
63  add_option
64  ("bface", "", 0,
65  "Respect the Maya \"double sided\" rendering flag to indicate whether "
66  "polygons should be double-sided or single-sided. Since this flag "
67  "is set to double-sided by default in Maya, it is often better to "
68  "ignore this flag (unless your modelers are diligent in turning it "
69  "off where it is not desired). If this flag is not specified, the "
70  "default is to treat all polygons as single-sided, unless an "
71  "egg object type of \"double-sided\" is set.",
72  &MayaToEggServer::dispatch_none, &_respect_maya_double_sided);
73 
74  add_option
75  ("suppress-vcolor", "", 0,
76  "Ignore vertex color for geometry that has a texture applied. "
77  "(This is the way Maya normally renders internally.) The egg flag "
78  "'vertex-color' may be applied to a particular model to override "
79  "this setting locally.",
80  &MayaToEggServer::dispatch_none, &_suppress_vertex_color);
81 
82  add_option
83  ("keep-uvs", "", 0,
84  "Convert all UV sets on all vertices, even those that do not appear "
85  "to be referenced by any textures.",
86  &MayaToEggServer::dispatch_none, &_keep_all_uvsets);
87 
88  add_option
89  ("round-uvs", "", 0,
90  "round up uv coordinates to the nearest 1/100th. i.e. -0.001 becomes"
91  "0.0; 0.444 becomes 0.44; 0.778 becomes 0.78.",
92  &MayaToEggServer::dispatch_none, &_round_uvs);
93 
94  add_option
95  ("trans", "type", 0,
96  "Specifies which transforms in the Maya file should be converted to "
97  "transforms in the egg file. The option may be one of all, model, "
98  "dcs, or none. The default is model, which means only transforms on "
99  "nodes that have the model flag or the dcs flag are preserved.",
100  &MayaToEggServer::dispatch_transform_type, NULL, &_transform_type);
101 
102  add_option
103  ("subroot", "name", 0,
104  "Specifies that only a subroot of the geometry in the Maya file should "
105  "be converted; specifically, the geometry under the node or nodes whose "
106  "name matches the parameter (which may include globbing characters "
107  "like * or ?). This parameter may be repeated multiple times to name "
108  "multiple roots. If it is omitted altogether, the entire file is "
109  "converted.",
110  &MayaToEggServer::dispatch_vector_string, NULL, &_subroots);
111 
112  add_option
113  ("subset", "name", 0,
114  "Specifies that only a subset of the geometry in the Maya file should "
115  "be converted; specifically, the geometry under the node or nodes whose "
116  "name matches the parameter (which may include globbing characters "
117  "like * or ?). This parameter may be repeated multiple times to name "
118  "multiple roots. If it is omitted altogether, the entire file is "
119  "converted.",
120  &MayaToEggServer::dispatch_vector_string, NULL, &_subsets);
121 
122  add_option
123  ("exclude", "name", 0,
124  "Specifies that a subset of the geometry in the Maya file should "
125  "not be converted; specifically, the geometry under the node or nodes whose "
126  "name matches the parameter (which may include globbing characters "
127  "like * or ?). This parameter may be repeated multiple times to name "
128  "multiple roots.",
129  &MayaToEggServer::dispatch_vector_string, NULL, &_excludes);
130 
131  add_option
132  ("ignore-slider", "name", 0,
133  "Specifies the name of a slider (blend shape deformer) that maya2egg "
134  "should not process. The slider will not be touched during conversion "
135  "and it will not become a part of the animation. This "
136  "parameter may including globbing characters, and it may be repeated "
137  "as needed.",
138  &MayaToEggServer::dispatch_vector_string, NULL, &_ignore_sliders);
139 
140  add_option
141  ("force-joint", "name", 0,
142  "Specifies the name of a DAG node that maya2egg "
143  "should treat as a joint, even if it does not appear to be a Maya joint "
144  "and does not appear to be animated.",
145  &MayaToEggServer::dispatch_vector_string, NULL, &_force_joints);
146 
147  add_option
148  ("v", "", 0,
149  "Increase verbosity. More v's means more verbose.",
150  &MayaToEggServer::dispatch_count, NULL, &_verbose);
151 
152  add_option
153  ("legacy-shaders", "", 0,
154  "Use this flag to turn off modern (Phong) shader generation"
155  "and treat all shaders as if they were Lamberts (legacy).",
156  &MayaToEggServer::dispatch_none, &_legacy_shader);
157 
158  // Unfortunately, the Maya API doesn't allow us to differentiate
159  // between relative and absolute pathnames--everything comes out as
160  // an absolute pathname, even if it is stored in the Maya file as a
161  // relative path. So we can't support -noabs.
162  remove_option("noabs");
163 
164  _verbose = 0;
165  _polygon_tolerance = 0.01;
166  _transform_type = MayaToEggConverter::TT_model;
167  _got_tbnauto = true;
168  qManager = new QueuedConnectionManager();
169  qListener = new QueuedConnectionListener(qManager, 0);
170  qReader = new QueuedConnectionReader(qManager, 0);
171  cWriter = new ConnectionWriter(qManager, 0);
172  dummy = new MayaToEggConverter();
173 
174  nout << "Initializing Maya...\n";
175  if (!dummy->open_api()) {
176  nout << "Unable to initialize Maya.\n";
177  exit(1);
178  }
179 }
180 ////////////////////////////////////////////////////////////////////
181 // Function: MayaToEggServer::Destructor
182 // Access: Public
183 // Description:
184 ////////////////////////////////////////////////////////////////////
185 MayaToEggServer::
186 ~MayaToEggServer() {
187  delete qManager;
188  delete qReader;
189  delete qListener;
190  delete cWriter;
191  delete dummy;
192 }
193 
194 ////////////////////////////////////////////////////////////////////
195 // Function: MayaToEggServer::run
196 // Access: Public
197 // Description:
198 ////////////////////////////////////////////////////////////////////
199 void MayaToEggServer::
200 run() {
201  // Make sure we have good clean data to start with
202  _data = new EggData();
203 
204  // Set the verbose level by using Notify.
205  if (_verbose >= 3) {
206  maya_cat->set_severity(NS_spam);
207  mayaegg_cat->set_severity(NS_spam);
208  } else if (_verbose >= 2) {
209  maya_cat->set_severity(NS_debug);
210  mayaegg_cat->set_severity(NS_debug);
211  } else if (_verbose >= 1) {
212  maya_cat->set_severity(NS_info);
213  mayaegg_cat->set_severity(NS_info);
214  }
215 
216  // Let's convert the output file to a full path before we initialize
217  // Maya, since Maya now has a nasty habit of changing the current
218  // directory.
219  if (_got_output_filename) {
220  _output_filename.make_absolute();
221  _path_replace->_path_directory.make_absolute();
222  }
223 
224  MayaToEggConverter converter(_program_name);
225 
226  // Copy in the command-line parameters.
227  converter._polygon_output = _polygon_output;
228  converter._polygon_tolerance = _polygon_tolerance;
229  converter._respect_maya_double_sided = _respect_maya_double_sided;
230  converter._always_show_vertex_color = !_suppress_vertex_color;
231  converter._keep_all_uvsets = _keep_all_uvsets;
232  converter._round_uvs = _round_uvs;
233  converter._transform_type = _transform_type;
234  converter._legacy_shader = _legacy_shader;
235 
236  vector_string::const_iterator si;
237  if (!_subroots.empty()) {
238  converter.clear_subroots();
239  for (si = _subroots.begin(); si != _subroots.end(); ++si) {
240  converter.add_subroot(GlobPattern(*si));
241  }
242  }
243 
244  if (!_subsets.empty()) {
245  converter.clear_subsets();
246  for (si = _subsets.begin(); si != _subsets.end(); ++si) {
247  converter.add_subset(GlobPattern(*si));
248  }
249  }
250 
251  if (!_excludes.empty()) {
252  converter.clear_excludes();
253  for (si = _excludes.begin(); si != _excludes.end(); ++si) {
254  converter.add_exclude(GlobPattern(*si));
255  }
256  }
257 
258  if (!_ignore_sliders.empty()) {
259  converter.clear_ignore_sliders();
260  for (si = _ignore_sliders.begin(); si != _ignore_sliders.end(); ++si) {
261  converter.add_ignore_slider(GlobPattern(*si));
262  }
263  }
264 
265  if (!_force_joints.empty()) {
266  converter.clear_force_joints();
267  for (si = _force_joints.begin(); si != _force_joints.end(); ++si) {
268  converter.add_force_joint(GlobPattern(*si));
269  }
270  }
271 
272  // Copy in the path and animation parameters.
273  apply_parameters(converter);
274 
275  // Set the coordinate system to match Maya's.
276  if (!_got_coordinate_system) {
277  _coordinate_system = converter._maya->get_coordinate_system();
278  }
279  _data->set_coordinate_system(_coordinate_system);
280 
281  converter.set_egg_data(_data);
282 
283  if (!converter.convert_file(_input_filename)) {
284  nout << "Errors in conversion.\n";
285  exit(1);
286  }
287 
288  // Use the standard Maya units, if the user didn't specify
289  // otherwise. This always returns centimeters, which is the way all
290  // Maya files are stored internally (and is the units returned by
291  // all of the API functions called here).
292  if (_input_units == DU_invalid) {
293  _input_units = converter.get_input_units();
294  }
295 
296  // Add the command line comment at the top of the egg file
297  append_command_comment(_data);
298 
299  write_egg_file();
300 
301  // Clean and out
302  close_output();
303  _verbose = 0;
304  _polygon_tolerance = 0.01;
305  _polygon_output = 0;
306  _transform_type = MayaToEggConverter::TT_model;
307  _subsets.clear();
308  _subroots.clear();
309  _input_units = DU_invalid;
310  _output_units = DU_invalid;
311  _excludes.clear();
312  _ignore_sliders.clear();
313  _force_joints.clear();
314  _got_transform = false;
315  _transform = LMatrix4d::ident_mat();
316  _normals_mode = NM_preserve;
317  _normals_threshold = 0.0;
318  _got_start_frame = false;
319  _got_end_frame = false;
320  _got_frame_inc = false;
321  _got_neutral_frame = false;
322  _got_input_frame_rate = false;
323  _got_output_frame_rate = false;
324  _got_output_filename = false;
325  _merge_externals = false;
326  _got_tbnall = false;
327  _got_tbnauto = false;
328  _got_transform = false;
329  _coordinate_system = CS_yup_right;
330  _noabs = false;
331  _program_args.clear();
332  _data->clear();
333  _animation_convert = AC_none;
334  _character_name = "";
335  dummy->clear();
336 }
337 
338 ////////////////////////////////////////////////////////////////////
339 // Function: MayaToEggServer::dispatch_transform_type
340 // Access: Protected, Static
341 // Description: Dispatches a parameter that expects a
342 // MayaToEggConverter::TransformType option.
343 ////////////////////////////////////////////////////////////////////
344 bool MayaToEggServer::
345 dispatch_transform_type(const string &opt, const string &arg, void *var) {
346  MayaToEggConverter::TransformType *ip = (MayaToEggConverter::TransformType *)var;
348 
349  if ((*ip) == MayaToEggConverter::TT_invalid) {
350  nout << "Invalid type for -" << opt << ": " << arg << "\n"
351  << "Valid types are all, model, dcs, and none.\n";
352  return false;
353  }
354 
355  return true;
356 }
357 
358 ////////////////////////////////////////////////////////////////////
359 // Function: MayaToEggServer::poll
360 // Access: Public
361 // Description: Checks for any network activity and handles it, if
362 // appropriate, and then returns. This must be called
363 // periodically
364 ////////////////////////////////////////////////////////////////////
366 poll() {
367  // Listen for new connections
368  qListener->poll();
369 
370  // If we have a new connection from a client create a new connection
371  // pointer and add it to the reader list
372  if (qListener->new_connection_available()) {
373  PT(Connection) con;
374  PT(Connection) rv;
375  NetAddress address;
376  if (qListener->get_new_connection(rv, address, con)) {
377  qReader->add_connection(con);
378  _clients.insert(con);
379  }
380  }
381 
382  // Check for reset clients
383  if (qManager->reset_connection_available()) {
384  PT(Connection) connection;
385  if (qManager->get_reset_connection(connection)) {
386  _clients.erase(connection);
387  qManager->close_connection(connection);
388  }
389  }
390 
391  // Poll the readers (created above) and if they have data process it
392  qReader->poll();
393  if (qReader->data_available()) {
394  // Grab the incomming data and unpack it
395  NetDatagram datagram;
396  if (qReader->get_data(datagram)) {
397  DatagramIterator data(datagram);
398  // First data should be the "argc" (argument count) from the client
399  int argc = data.get_uint8();
400 
401  // Now we have to get clever because the rest of the data comes as strings
402  // and parse_command_line() expects arguments of the standard argc, argv*[]
403  // variety.
404  // First, we need a string vector to hold all the strings from the datagram.
405  // We also need a char * array to keep track of all the pointers we're gonna
406  // malloc. Needed later for cleanup.
407  vector_string vargv;
408  vector<char *> buffers;
409 
410  // Get the strings from the datagram and put them into the string vector
411  int i;
412  for ( i = 0; i < argc; i++ ) {
413  vargv.push_back(data.get_string());
414  }
415 
416  // Last string is the current directory the client was run from. Not part of
417  // the argument list, but we still need it
418  string cwd = data.get_string();
419 
420  // We allocate some memory to hold the pointers to the pointers we're going to
421  // pass in to parse_command_line().
422  char ** cargv = (char**) malloc(sizeof(char**) * argc);
423 
424  // Loop through the string arguments we got from the datagram and convert
425  // them to const char *'s. parse_command_line() expects char *'s, so we have
426  // to copy these const versions into fresh char *, since there is no casting
427  // from const char * to char *.
428  for ( i = 0; i < argc; i++) {
429  // string to const char *
430  const char * cptr = vargv[i].c_str();
431  // memory allocated for a new char * of size of the string
432  char * buffer = (char*) malloc(vargv[i].capacity()+1);
433  // Copy the const char * to the char *
434  strcpy(buffer, cptr);
435  // put this into the arry we defined above. This is what will eventually
436  // be passed to parse_command_line()
437  cargv[i] = buffer;
438  // keep track of the pointers to the allocated memory for cleanup later
439  buffers.push_back(buffer);
440  }
441  // Change to the client's current dir
442 #ifdef WIN64_VC
443  _chdir(cwd.c_str());
444 #else
445  chdir(cwd.c_str());
446 #endif
447 
448  // Pass in the 'new' argc and argv we got from the client
449  this->parse_command_line(argc, cargv);
450  // Actually run the damn thing
451  this->run();
452 
453  // Cleanup
454  // First, release the string vector
455  vargv.clear();
456  // No, iterate through the char * vector and cleanup the malloc'd
457  // pointers
458  vector<char *>::iterator vi;
459  for ( vi = buffers.begin() ; vi != buffers.end(); vi++) {
460  free(*vi);
461  }
462  // Clean up the malloc'd pointer pointer
463  free(cargv);
464  } // qReader->get_data
465 
466  Clients::iterator ci;
467  for (ci = _clients.begin(); ci != _clients.end(); ++ci) {
468  qManager->close_connection(*ci);
469  }
470  } // qReader->data_available
471 } // poll
472 
473 int main(int argc, char *argv[]) {
474  // We don't want pystub on linux, since it gives problems with Maya's python.
475 #ifdef _WIN32
476  // A call to pystub() to force libpystub.so to be linked in.
477  pystub();
478 #endif
479 
480  MayaToEggServer prog;
481  // Open a rendezvous port for receiving new connections from the client
482  PT(Connection) rend = prog.qManager->open_TCP_server_rendezvous(4242, 50);
483  if (rend.is_null()) {
484  nout << "port opened fail";
485  }
486 
487  // Add this connection to the listeners list
488  prog.qListener->add_connection(rend);
489 
490  // Main loop. Keep polling for connections, but don't eat up all the CPU.
491  while (true) {
492  prog.poll();
494  }
495  return 0;
496 }
497 
bool open_api(bool revert_directory=true)
Attempts to open the Maya API if it was not already open, and returns true if successful, or false if there is an error.
A specific kind of Datagram, especially for sending across or receiving from a network.
Definition: netDatagram.h:43
static TransformType string_transform_type(const string &arg)
Returns the TransformType value corresponding to the indicated string, or TT_invalid.
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_...
This flavor of ConnectionManager will queue up all of the reset-connection messages from the Connecti...
void add_animation_options()
Adds options appropriate to animation packages.
void add_normals_options()
Adds -no, -np, etc.
Definition: eggBase.cxx:63
void poll()
Checks for any network activity and handles it, if appropriate, and then returns. ...
bool get_reset_connection(PT(Connection) &connection)
If a previous call to reset_connection_available() returned true, this function will return informati...
void close_output()
Closes the output stream previously opened by get_output().
void clear()
Frees all of the Maya pointers kept within this object, in preparation for loading a new scene or rel...
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:248
string get_string()
Extracts a variable-length string.
bool reset_connection_available() const
Returns true if one of the readers/writers/listeners reported a connection reset recently.
void poll()
Explicitly polls the available sockets to see if any of them have any noise.
bool data_available()
Returns true if a datagram is available on the queue; call get_data() to extract the datagram...
static const LMatrix4d & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:5168
bool new_connection_available()
Returns true if a new connection was recently established; the connection information may then be ret...
This class handles threaded delivery of datagrams to various TCP or UDP sockets.
void write_egg_file()
Writes out the egg file as the normal result of the program.
Definition: eggWriter.cxx:193
This class supervises the construction of an EggData structure from a single Maya file...
bool get_data(NetDatagram &result)
If a previous call to data_available() returned true, this function will return the datagram that has...
bool add_connection(Connection *connection)
Adds a new socket to the list of sockets the ConnectionReader will monitor.
This flavor of ConnectionReader will read from its sockets and queue up all of the datagrams read for...
void add_transform_options()
Adds -TS, -TT, etc.
Definition: eggBase.cxx:135
This is the general base class for a file-converter program that reads some model file format and gen...
A class to retrieve the individual data elements previously stored in a Datagram. ...
Represents a single TCP or UDP socket for input or output.
Definition: connection.h:32
bool close_connection(const PT(Connection) &connection)
Terminates a UDP or TCP socket previously opened.
bool get_new_connection(PT(Connection) &rendezvous, NetAddress &address, PT(Connection) &new_connection)
If a previous call to new_connection_available() returned true, this function will return information...
void add_units_options()
Adds -ui and -uo as valid options for this program.
This class can be used to test for string matches against standard Unix-shell filename globbing conve...
Definition: globPattern.h:37
Represents a network address to which UDP packets may be sent or to which a TCP socket may be bound...
Definition: netAddress.h:27
This flavor of ConnectionListener will queue up all of the TCP connections it established for later d...