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