Panda3D
 All Classes Functions Variables Enumerations
mayaToEgg_server.cxx
00001 // Filename: mayaToEgg_server.cxx
00002 // Adapted by: cbrunner (09Nov09)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #if defined(WIN32_VC) || defined(WIN64_VC)
00016 #include <direct.h>  // for chdir
00017 #endif
00018 #include "mayaToEgg_server.h"
00019 #include "mayaToEggConverter.h"
00020 #include "config_mayaegg.h"
00021 #include "config_maya.h"  // for maya_cat
00022 #include "globPattern.h"
00023 #ifdef _WIN32
00024   #include "pystub.h"
00025 #endif
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //     Function: MayaToEggServer::Constructor
00029 //       Access: Public
00030 //  Description:
00031 ////////////////////////////////////////////////////////////////////
00032 MayaToEggServer::
00033 MayaToEggServer() :
00034   SomethingToEgg("Maya", ".mb")
00035 {
00036   add_path_replace_options();
00037   add_path_store_options();
00038   add_animation_options();
00039   add_units_options();
00040   add_normals_options();
00041   add_transform_options();
00042 
00043   set_program_description
00044     ("This program converts Maya model files to egg.  Static and animatable "
00045      "models can be converted, with polygon or NURBS output.  Animation tables "
00046      "can also be generated to apply to an animatable model.");
00047 
00048   add_option
00049     ("p", "", 0,
00050      "Generate polygon output only.  Tesselate all NURBS surfaces to "
00051      "polygons via the built-in Maya tesselator.  The tesselation will "
00052      "be based on the tolerance factor given by -ptol.",
00053      &MayaToEggServer::dispatch_none, &_polygon_output);
00054 
00055   add_option
00056     ("ptol", "tolerance", 0,
00057      "Specify the fit tolerance for Maya polygon tesselation.  The smaller "
00058      "the number, the more polygons will be generated.  The default is "
00059      "0.01.",
00060      &MayaToEggServer::dispatch_double, NULL, &_polygon_tolerance);
00061 
00062   add_option
00063     ("bface", "", 0,
00064      "Respect the Maya \"double sided\" rendering flag to indicate whether "
00065      "polygons should be double-sided or single-sided.  Since this flag "
00066      "is set to double-sided by default in Maya, it is often better to "
00067      "ignore this flag (unless your modelers are diligent in turning it "
00068      "off where it is not desired).  If this flag is not specified, the "
00069      "default is to treat all polygons as single-sided, unless an "
00070      "egg object type of \"double-sided\" is set.",
00071      &MayaToEggServer::dispatch_none, &_respect_maya_double_sided);
00072 
00073   add_option
00074     ("suppress-vcolor", "", 0,
00075      "Ignore vertex color for geometry that has a texture applied.  "
00076      "(This is the way Maya normally renders internally.)  The egg flag "
00077      "'vertex-color' may be applied to a particular model to override "
00078      "this setting locally.",
00079      &MayaToEggServer::dispatch_none, &_suppress_vertex_color);
00080 
00081   add_option
00082     ("keep-uvs", "", 0,
00083      "Convert all UV sets on all vertices, even those that do not appear "
00084      "to be referenced by any textures.",
00085      &MayaToEggServer::dispatch_none, &_keep_all_uvsets);
00086 
00087   add_option
00088     ("round-uvs", "", 0,
00089      "round up uv coordinates to the nearest 1/100th. i.e. -0.001 becomes"
00090      "0.0; 0.444 becomes 0.44; 0.778 becomes 0.78.",
00091      &MayaToEggServer::dispatch_none, &_round_uvs);
00092 
00093   add_option
00094     ("trans", "type", 0,
00095      "Specifies which transforms in the Maya file should be converted to "
00096      "transforms in the egg file.  The option may be one of all, model, "
00097      "dcs, or none.  The default is model, which means only transforms on "
00098      "nodes that have the model flag or the dcs flag are preserved.",
00099      &MayaToEggServer::dispatch_transform_type, NULL, &_transform_type);
00100 
00101   add_option
00102     ("subroot", "name", 0,
00103      "Specifies that only a subroot of the geometry in the Maya file should "
00104      "be converted; specifically, the geometry under the node or nodes whose "
00105      "name matches the parameter (which may include globbing characters "
00106      "like * or ?).  This parameter may be repeated multiple times to name "
00107      "multiple roots.  If it is omitted altogether, the entire file is "
00108      "converted.",
00109      &MayaToEggServer::dispatch_vector_string, NULL, &_subroots);
00110 
00111   add_option
00112     ("subset", "name", 0,
00113      "Specifies that only a subset of the geometry in the Maya file should "
00114      "be converted; specifically, the geometry under the node or nodes whose "
00115      "name matches the parameter (which may include globbing characters "
00116      "like * or ?).  This parameter may be repeated multiple times to name "
00117      "multiple roots.  If it is omitted altogether, the entire file is "
00118      "converted.",
00119      &MayaToEggServer::dispatch_vector_string, NULL, &_subsets);
00120 
00121   add_option
00122     ("exclude", "name", 0,
00123      "Specifies that a subset of the geometry in the Maya file should "
00124      "not be converted; specifically, the geometry under the node or nodes whose "
00125      "name matches the parameter (which may include globbing characters "
00126      "like * or ?).  This parameter may be repeated multiple times to name "
00127      "multiple roots.",
00128      &MayaToEggServer::dispatch_vector_string, NULL, &_excludes);
00129 
00130   add_option
00131     ("ignore-slider", "name", 0,
00132      "Specifies the name of a slider (blend shape deformer) that maya2egg "
00133      "should not process.  The slider will not be touched during conversion "
00134      "and it will not become a part of the animation.  This "
00135      "parameter may including globbing characters, and it may be repeated "
00136      "as needed.",
00137      &MayaToEggServer::dispatch_vector_string, NULL, &_ignore_sliders);
00138 
00139   add_option
00140     ("force-joint", "name", 0,
00141      "Specifies the name of a DAG node that maya2egg "
00142      "should treat as a joint, even if it does not appear to be a Maya joint "
00143      "and does not appear to be animated.",
00144      &MayaToEggServer::dispatch_vector_string, NULL, &_force_joints);
00145 
00146   add_option
00147     ("v", "", 0,
00148      "Increase verbosity.  More v's means more verbose.",
00149      &MayaToEggServer::dispatch_count, NULL, &_verbose);
00150 
00151   add_option
00152     ("legacy-shaders", "", 0,
00153      "Use this flag to turn off modern (Phong) shader generation"
00154      "and treat all shaders as if they were Lamberts (legacy).",
00155      &MayaToEggServer::dispatch_none, &_legacy_shader);
00156 
00157   // Unfortunately, the Maya API doesn't allow us to differentiate
00158   // between relative and absolute pathnames--everything comes out as
00159   // an absolute pathname, even if it is stored in the Maya file as a
00160   // relative path.  So we can't support -noabs.
00161   remove_option("noabs");
00162 
00163   _verbose = 0;
00164   _polygon_tolerance = 0.01;
00165   _transform_type = MayaToEggConverter::TT_model;
00166   _got_tbnauto = true;
00167   qManager = new QueuedConnectionManager();
00168   qListener = new QueuedConnectionListener(qManager, 0);
00169   qReader = new QueuedConnectionReader(qManager, 0);
00170   cWriter = new ConnectionWriter(qManager, 0);
00171   dummy = new MayaToEggConverter();
00172   
00173   nout << "Initializing Maya...\n";
00174   if (!dummy->open_api()) {
00175     nout << "Unable to initialize Maya.\n";
00176     exit(1);
00177   }
00178 }
00179 ////////////////////////////////////////////////////////////////////
00180 //     Function: MayaToEggServer::Destructor
00181 //       Access: Public
00182 //  Description:
00183 ////////////////////////////////////////////////////////////////////
00184 MayaToEggServer::
00185 ~MayaToEggServer() {
00186   delete qManager;
00187   delete qReader;
00188   delete qListener;
00189   delete cWriter;
00190   delete dummy;
00191 }
00192 
00193 ////////////////////////////////////////////////////////////////////
00194 //     Function: MayaToEggServer::run
00195 //       Access: Public
00196 //  Description:
00197 ////////////////////////////////////////////////////////////////////
00198 void MayaToEggServer::
00199 run() {
00200   // Make sure we have good clean data to start with
00201   _data = new EggData();
00202 
00203   // Set the verbose level by using Notify.
00204   if (_verbose >= 3) {
00205     maya_cat->set_severity(NS_spam);
00206     mayaegg_cat->set_severity(NS_spam);
00207   } else if (_verbose >= 2) {
00208     maya_cat->set_severity(NS_debug);
00209     mayaegg_cat->set_severity(NS_debug);
00210   } else if (_verbose >= 1) {
00211     maya_cat->set_severity(NS_info);
00212     mayaegg_cat->set_severity(NS_info);
00213   }
00214 
00215   // Let's convert the output file to a full path before we initialize
00216   // Maya, since Maya now has a nasty habit of changing the current
00217   // directory.
00218   if (_got_output_filename) {
00219     _output_filename.make_absolute();
00220     _path_replace->_path_directory.make_absolute();
00221   }
00222 
00223   MayaToEggConverter converter(_program_name);
00224 
00225   // Copy in the command-line parameters.
00226   converter._polygon_output = _polygon_output;
00227   converter._polygon_tolerance = _polygon_tolerance;
00228   converter._respect_maya_double_sided = _respect_maya_double_sided;
00229   converter._always_show_vertex_color = !_suppress_vertex_color;
00230   converter._keep_all_uvsets = _keep_all_uvsets;
00231   converter._round_uvs = _round_uvs;
00232   converter._transform_type = _transform_type;
00233   converter._legacy_shader = _legacy_shader;
00234 
00235   vector_string::const_iterator si;
00236   if (!_subroots.empty()) {
00237     converter.clear_subroots();
00238     for (si = _subroots.begin(); si != _subroots.end(); ++si) {
00239       converter.add_subroot(GlobPattern(*si));
00240     }
00241   }
00242 
00243   if (!_subsets.empty()) {
00244     converter.clear_subsets();
00245     for (si = _subsets.begin(); si != _subsets.end(); ++si) {
00246       converter.add_subset(GlobPattern(*si));
00247     }
00248   }
00249 
00250   if (!_excludes.empty()) {
00251     converter.clear_excludes();
00252     for (si = _excludes.begin(); si != _excludes.end(); ++si) {
00253       converter.add_exclude(GlobPattern(*si));
00254     }
00255   }
00256 
00257   if (!_ignore_sliders.empty()) {
00258     converter.clear_ignore_sliders();
00259     for (si = _ignore_sliders.begin(); si != _ignore_sliders.end(); ++si) {
00260       converter.add_ignore_slider(GlobPattern(*si));
00261     }
00262   }
00263 
00264   if (!_force_joints.empty()) {
00265     converter.clear_force_joints();
00266     for (si = _force_joints.begin(); si != _force_joints.end(); ++si) {
00267       converter.add_force_joint(GlobPattern(*si));
00268     }
00269   }
00270 
00271   // Copy in the path and animation parameters.
00272   apply_parameters(converter);
00273 
00274   // Set the coordinate system to match Maya's.
00275   if (!_got_coordinate_system) {
00276     _coordinate_system = converter._maya->get_coordinate_system();
00277   }
00278   _data->set_coordinate_system(_coordinate_system);
00279 
00280   converter.set_egg_data(_data);
00281 
00282   if (!converter.convert_file(_input_filename)) {
00283     nout << "Errors in conversion.\n";
00284     exit(1);
00285   }
00286 
00287   // Use the standard Maya units, if the user didn't specify
00288   // otherwise.  This always returns centimeters, which is the way all
00289   // Maya files are stored internally (and is the units returned by
00290   // all of the API functions called here).
00291   if (_input_units == DU_invalid) {
00292     _input_units = converter.get_input_units();
00293   }
00294 
00295   // Add the command line comment at the top of the egg file
00296   append_command_comment(_data);
00297 
00298   write_egg_file();
00299   
00300   // Clean and out
00301   close_output();
00302   _verbose = 0;
00303   _polygon_tolerance = 0.01;
00304   _polygon_output = 0;
00305   _transform_type = MayaToEggConverter::TT_model;
00306   _subsets.clear();
00307   _subroots.clear();
00308   _input_units = DU_invalid;
00309   _output_units = DU_invalid;
00310   _excludes.clear();
00311   _ignore_sliders.clear();
00312   _force_joints.clear();
00313   _got_transform = false;
00314   _transform = LMatrix4d::ident_mat();
00315   _normals_mode = NM_preserve;
00316   _normals_threshold = 0.0;
00317   _got_start_frame = false;
00318   _got_end_frame = false;
00319   _got_frame_inc = false;
00320   _got_neutral_frame = false;
00321   _got_input_frame_rate = false;
00322   _got_output_frame_rate = false;
00323   _got_output_filename = false;
00324   _merge_externals = false;
00325   _got_tbnall = false;
00326   _got_tbnauto = false;
00327   _got_transform = false;
00328   _coordinate_system = CS_yup_right;
00329   _noabs = false;
00330   _program_args.clear();
00331   _data->clear();
00332   _animation_convert = AC_none;
00333   _character_name = "";
00334   dummy->clear();
00335 }
00336 
00337 ////////////////////////////////////////////////////////////////////
00338 //     Function: MayaToEggServer::dispatch_transform_type
00339 //       Access: Protected, Static
00340 //  Description: Dispatches a parameter that expects a
00341 //               MayaToEggConverter::TransformType option.
00342 ////////////////////////////////////////////////////////////////////
00343 bool MayaToEggServer::
00344 dispatch_transform_type(const string &opt, const string &arg, void *var) {
00345   MayaToEggConverter::TransformType *ip = (MayaToEggConverter::TransformType *)var;
00346   (*ip) = MayaToEggConverter::string_transform_type(arg);
00347 
00348   if ((*ip) == MayaToEggConverter::TT_invalid) {
00349     nout << "Invalid type for -" << opt << ": " << arg << "\n"
00350          << "Valid types are all, model, dcs, and none.\n";
00351     return false;
00352   }
00353 
00354   return true;
00355 }
00356 
00357 ////////////////////////////////////////////////////////////////////
00358 //     Function: MayaToEggServer::poll
00359 //       Access: Public
00360 //  Description: Checks for any network activity and handles it, if
00361 //               appropriate, and then returns.  This must be called
00362 //               periodically
00363 ////////////////////////////////////////////////////////////////////
00364 void MayaToEggServer::
00365 poll() {
00366   // Listen for new connections
00367   qListener->poll();
00368 
00369   // If we have a new connection from a client create a new connection
00370   // pointer and add it to the reader list
00371   if (qListener->new_connection_available()) {
00372     PT(Connection) con;
00373     PT(Connection) rv;
00374     NetAddress address;
00375     if (qListener->get_new_connection(rv, address, con)) {
00376       qReader->add_connection(con);
00377       _clients.insert(con);
00378     }
00379   }
00380 
00381   // Check for reset clients
00382   if (qManager->reset_connection_available()) {
00383     PT(Connection) connection;
00384     if (qManager->get_reset_connection(connection)) {
00385       _clients.erase(connection);
00386       qManager->close_connection(connection);
00387     }
00388   }
00389   
00390   // Poll the readers (created above) and if they have data process it
00391   qReader->poll();
00392   if (qReader->data_available()) {
00393     // Grab the incomming data and unpack it
00394     NetDatagram datagram;
00395     if (qReader->get_data(datagram)) {
00396       DatagramIterator data(datagram);
00397       // First data should be the "argc" (argument count) from the client
00398       int argc = data.get_uint8();
00399 
00400       // Now we have to get clever because the rest of the data comes as strings
00401       // and parse_command_line() expects arguments of the standard argc, argv*[]
00402       // variety.
00403       // First, we need a string vector to hold all the strings from the datagram.
00404       // We also need a char * array to keep track of all the pointers we're gonna
00405       // malloc.  Needed later for cleanup.
00406       vector_string vargv;
00407       vector<char *> buffers;
00408 
00409       // Get the strings from the datagram and put them into the string vector
00410       int i;
00411       for ( i = 0; i < argc; i++ ) {
00412         vargv.push_back(data.get_string());
00413       }
00414 
00415       // Last string is the current directory the client was run from. Not part of
00416       // the argument list, but we still need it
00417       string cwd = data.get_string();
00418 
00419       // We allocate some memory to hold the pointers to the pointers we're going to
00420       // pass in to parse_command_line().
00421       char ** cargv = (char**) malloc(sizeof(char**) * argc);
00422 
00423       // Loop through the string arguments we got from the datagram and convert
00424       // them to const char *'s.  parse_command_line() expects char *'s, so we have
00425       // to copy these const versions into fresh char *, since there is no casting
00426       // from const char * to char *.
00427       for ( i = 0; i < argc; i++) {
00428         // string to const char *
00429         const char * cptr = vargv[i].c_str();
00430         // memory allocated for a new char * of size of the string
00431         char * buffer = (char*) malloc(vargv[i].capacity()+1);
00432         // Copy the const char * to the char *
00433         strcpy(buffer, cptr);
00434         // put this into the arry we defined above.  This is what will eventually
00435         // be passed to parse_command_line()
00436         cargv[i] = buffer;
00437         // keep track of the pointers to the  allocated memory for cleanup later
00438         buffers.push_back(buffer);
00439       }
00440       // Change to the client's current dir
00441 #ifdef WIN64_VC
00442       _chdir(cwd.c_str());
00443 #else
00444       chdir(cwd.c_str());
00445 #endif
00446 
00447       // Pass in the 'new' argc and argv we got from the client
00448       this->parse_command_line(argc, cargv);
00449       // Actually run the damn thing
00450       this->run();
00451 
00452       // Cleanup
00453       // First, release the string vector
00454       vargv.clear();
00455       // No, iterate through the char * vector and cleanup the malloc'd
00456       // pointers
00457       vector<char *>::iterator vi;
00458       for ( vi = buffers.begin() ; vi != buffers.end(); vi++) {
00459         free(*vi);
00460       }
00461       // Clean up the malloc'd pointer pointer
00462       free(cargv);
00463     } // qReader->get_data
00464 
00465     Clients::iterator ci;
00466     for (ci = _clients.begin(); ci != _clients.end(); ++ci) {
00467       qManager->close_connection(*ci);
00468     }
00469   } // qReader->data_available
00470 } // poll
00471 
00472 int main(int argc, char *argv[]) {
00473   // We don't want pystub on linux, since it gives problems with Maya's python.
00474 #ifdef _WIN32
00475   // A call to pystub() to force libpystub.so to be linked in.
00476   pystub();
00477 #endif
00478 
00479   MayaToEggServer prog;
00480   // Open a rendezvous port for receiving new connections from the client
00481   PT(Connection) rend = prog.qManager->open_TCP_server_rendezvous(4242, 50);
00482   if (rend.is_null()) {
00483     nout << "port opened fail";
00484   }
00485 
00486   // Add this connection to the listeners list
00487   prog.qListener->add_connection(rend);
00488 
00489   // Main loop.  Keep polling for connections, but don't eat up all the CPU.
00490   while (true) {
00491     prog.poll();
00492     Thread::force_yield();
00493   }
00494   return 0;
00495 }
00496 
 All Classes Functions Variables Enumerations