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 }
QueuedConnectionReader::data_available
bool data_available()
Returns true if a datagram is available on the queue; call get_data() to extract the datagram.
Definition: queuedConnectionReader.cxx:50
config_mayaegg.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SomethingToEgg::add_animation_options
void add_animation_options()
Adds options appropriate to animation packages.
Definition: somethingToEgg.cxx:107
WithOutputFile::close_output
void close_output()
Closes the output stream previously opened by get_output().
Definition: withOutputFile.cxx:106
EggBase::add_normals_options
void add_normals_options()
Adds -no, -np, etc.
Definition: eggBase.cxx:59
MayaToEggConverter::string_transform_type
static TransformType string_transform_type(const std::string &arg)
Returns the TransformType value corresponding to the indicated string, or TT_invalid.
Definition: mayaToEggConverter.cxx:3137
MayaToEggServer
Definition: mayaToEgg_server.h:28
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
QueuedConnectionReader::get_data
bool get_data(NetDatagram &result)
If a previous call to data_available() returned true, this function will return the datagram that has...
Definition: queuedConnectionReader.cxx:67
QueuedConnectionManager::get_reset_connection
bool get_reset_connection(PT(Connection) &connection)
If a previous call to reset_connection_available() returned true, this function will return informati...
Definition: queuedConnectionManager.cxx:67
Connection
Represents a single TCP or UDP socket for input or output.
Definition: connection.h:29
MayaToEggServer::poll
void poll()
Checks for any network activity and handles it, if appropriate, and then returns.
Definition: mayaToEgg_server.cxx:350
QueuedConnectionManager::reset_connection_available
bool reset_connection_available() const
Returns true if one of the readers/writers/listeners reported a connection reset recently.
Definition: queuedConnectionManager.cxx:47
MayaToEggConverter::clear
void clear()
Frees all of the Maya pointers kept within this object, in preparation for loading a new scene or rel...
Definition: mayaToEggConverter.cxx:567
ConnectionReader::add_connection
bool add_connection(Connection *connection)
Adds a new socket to the list of sockets the ConnectionReader will monitor.
Definition: connectionReader.cxx:176
QueuedConnectionListener
This flavor of ConnectionListener will queue up all of the TCP connections it established for later d...
Definition: queuedConnectionListener.h:46
mayaToEgg_server.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ProgramBase::parse_command_line
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_...
Definition: programBase.cxx:274
config_maya.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaToEggConverter
This class supervises the construction of an EggData structure from a single Maya file,...
Definition: mayaToEggConverter.h:52
EggData
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
SomethingToEgg::add_units_options
void add_units_options()
Adds -ui and -uo as valid options for this program.
Definition: somethingToEgg.cxx:87
EggBase::add_transform_options
void add_transform_options()
Adds -TS, -TT, etc.
Definition: eggBase.cxx:126
NetAddress
Represents a network address to which UDP packets may be sent or to which a TCP socket may be bound.
Definition: netAddress.h:25
ConnectionWriter
This class handles threaded delivery of datagrams to various TCP or UDP sockets.
Definition: connectionWriter.h:35
QueuedConnectionListener::get_new_connection
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...
Definition: queuedConnectionListener.cxx:63
QueuedConnectionListener::new_connection_available
bool new_connection_available()
Returns true if a new connection was recently established; the connection information may then be ret...
Definition: queuedConnectionListener.cxx:43
globPattern.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
mayaToEggConverter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ConnectionReader::poll
void poll()
Explicitly polls the available sockets to see if any of them have any noise.
Definition: connectionReader.cxx:260
NetDatagram
A specific kind of Datagram, especially for sending across or receiving from a network.
Definition: netDatagram.h:40
QueuedConnectionManager
This flavor of ConnectionManager will queue up all of the reset-connection messages from the Connecti...
Definition: queuedConnectionManager.h:36
SomethingToEgg
This is the general base class for a file-converter program that reads some model file format and gen...
Definition: somethingToEgg.h:29
EggWriter::write_egg_file
void write_egg_file()
Writes out the egg file as the normal result of the program.
Definition: eggWriter.cxx:177
QueuedConnectionReader
This flavor of ConnectionReader will read from its sockets and queue up all of the datagrams read for...
Definition: queuedConnectionReader.h:34
GlobPattern
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
Thread::force_yield
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:201