Panda3D

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     ("copytex","dir",0,
00095     "copy the textures to a ""Textures"" sub directory relative to the written out egg file."
00096     """dir"" is a sub directory in the same format as those used by -pr, etc." ,
00097      &MayaToEggServer::dispatch_filename, &_texture_copy, &_texture_out_dir);
00098 
00099   add_option
00100     ("trans", "type", 0,
00101      "Specifies which transforms in the Maya file should be converted to "
00102      "transforms in the egg file.  The option may be one of all, model, "
00103      "dcs, or none.  The default is model, which means only transforms on "
00104      "nodes that have the model flag or the dcs flag are preserved.",
00105      &MayaToEggServer::dispatch_transform_type, NULL, &_transform_type);
00106 
00107   add_option
00108     ("subroot", "name", 0,
00109      "Specifies that only a subroot of the geometry in the Maya file should "
00110      "be converted; specifically, the geometry under the node or nodes whose "
00111      "name matches the parameter (which may include globbing characters "
00112      "like * or ?).  This parameter may be repeated multiple times to name "
00113      "multiple roots.  If it is omitted altogether, the entire file is "
00114      "converted.",
00115      &MayaToEggServer::dispatch_vector_string, NULL, &_subroots);
00116 
00117   add_option
00118     ("subset", "name", 0,
00119      "Specifies that only a subset of the geometry in the Maya file should "
00120      "be converted; specifically, the geometry under the node or nodes whose "
00121      "name matches the parameter (which may include globbing characters "
00122      "like * or ?).  This parameter may be repeated multiple times to name "
00123      "multiple roots.  If it is omitted altogether, the entire file is "
00124      "converted.",
00125      &MayaToEggServer::dispatch_vector_string, NULL, &_subsets);
00126 
00127   add_option
00128     ("exclude", "name", 0,
00129      "Specifies that a subset of the geometry in the Maya file should "
00130      "not be converted; specifically, the geometry under the node or nodes whose "
00131      "name matches the parameter (which may include globbing characters "
00132      "like * or ?).  This parameter may be repeated multiple times to name "
00133      "multiple roots.",
00134      &MayaToEggServer::dispatch_vector_string, NULL, &_excludes);
00135 
00136   add_option
00137     ("ignore-slider", "name", 0,
00138      "Specifies the name of a slider (blend shape deformer) that maya2egg "
00139      "should not process.  The slider will not be touched during conversion "
00140      "and it will not become a part of the animation.  This "
00141      "parameter may including globbing characters, and it may be repeated "
00142      "as needed.",
00143      &MayaToEggServer::dispatch_vector_string, NULL, &_ignore_sliders);
00144 
00145   add_option
00146     ("force-joint", "name", 0,
00147      "Specifies the name of a DAG node that maya2egg "
00148      "should treat as a joint, even if it does not appear to be a Maya joint "
00149      "and does not appear to be animated.",
00150      &MayaToEggServer::dispatch_vector_string, NULL, &_force_joints);
00151 
00152   add_option
00153     ("v", "", 0,
00154      "Increase verbosity.  More v's means more verbose.",
00155      &MayaToEggServer::dispatch_count, NULL, &_verbose);
00156 
00157   add_option
00158     ("legacy-shaders", "", 0,
00159      "Use this flag to turn off modern (Phong) shader generation"
00160      "and treat all shaders as if they were Lamberts (legacy).",
00161      &MayaToEggServer::dispatch_none, &_legacy_shader);
00162 
00163   // Unfortunately, the Maya API doesn't allow us to differentiate
00164   // between relative and absolute pathnames--everything comes out as
00165   // an absolute pathname, even if it is stored in the Maya file as a
00166   // relative path.  So we can't support -noabs.
00167   remove_option("noabs");
00168 
00169   _verbose = 0;
00170   _polygon_tolerance = 0.01;
00171   _transform_type = MayaToEggConverter::TT_model;
00172   _got_tbnauto = true;
00173   qManager = new QueuedConnectionManager();
00174   qListener = new QueuedConnectionListener(qManager, 0);
00175   qReader = new QueuedConnectionReader(qManager, 0);
00176   cWriter = new ConnectionWriter(qManager, 0);
00177   dummy = new MayaToEggConverter();
00178   
00179   nout << "Initializing Maya...\n";
00180   if (!dummy->open_api()) {
00181     nout << "Unable to initialize Maya.\n";
00182     exit(1);
00183   }
00184 }
00185 ////////////////////////////////////////////////////////////////////
00186 //     Function: MayaToEggServer::Destructor
00187 //       Access: Public
00188 //  Description:
00189 ////////////////////////////////////////////////////////////////////
00190 MayaToEggServer::
00191 ~MayaToEggServer() {
00192   delete qManager;
00193   delete qReader;
00194   delete qListener;
00195   delete cWriter;
00196   delete dummy;
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: MayaToEggServer::run
00201 //       Access: Public
00202 //  Description:
00203 ////////////////////////////////////////////////////////////////////
00204 void MayaToEggServer::
00205 run() {
00206   // Make sure we have good clean data to start with
00207   _data = new EggData();
00208 
00209   // Set the verbose level by using Notify.
00210   if (_verbose >= 3) {
00211     maya_cat->set_severity(NS_spam);
00212     mayaegg_cat->set_severity(NS_spam);
00213   } else if (_verbose >= 2) {
00214     maya_cat->set_severity(NS_debug);
00215     mayaegg_cat->set_severity(NS_debug);
00216   } else if (_verbose >= 1) {
00217     maya_cat->set_severity(NS_info);
00218     mayaegg_cat->set_severity(NS_info);
00219   }
00220 
00221   // Let's convert the output file to a full path before we initialize
00222   // Maya, since Maya now has a nasty habit of changing the current
00223   // directory.
00224   if (_got_output_filename) {
00225     _output_filename.make_absolute();
00226     //conjunct the relative output path with output file's dir weifengh
00227     if (_texture_out_dir.is_local()) {
00228       Filename tempdir = _output_filename.get_dirname() + "/";
00229       _texture_out_dir = tempdir + _texture_out_dir;
00230     }
00231   }
00232 
00233   // So our relative path names come out correctly
00234   _path_replace->_path_directory = get_output_filename().get_dirname();
00235 
00236   MayaToEggConverter converter(_program_name);
00237 
00238   // Copy in the command-line parameters.
00239   converter._polygon_output = _polygon_output;
00240   converter._polygon_tolerance = _polygon_tolerance;
00241   converter._respect_maya_double_sided = _respect_maya_double_sided;
00242   converter._always_show_vertex_color = !_suppress_vertex_color;
00243   converter._keep_all_uvsets = _keep_all_uvsets;
00244   converter._round_uvs = _round_uvs;
00245   converter._transform_type = _transform_type;
00246   converter._texture_copy = _texture_copy;
00247   converter._texture_out_dir = _texture_out_dir;
00248   converter._legacy_shader = _legacy_shader;
00249 
00250   vector_string::const_iterator si;
00251   if (!_subroots.empty()) {
00252     converter.clear_subroots();
00253     for (si = _subroots.begin(); si != _subroots.end(); ++si) {
00254       converter.add_subroot(GlobPattern(*si));
00255     }
00256   }
00257 
00258   if (!_subsets.empty()) {
00259     converter.clear_subsets();
00260     for (si = _subsets.begin(); si != _subsets.end(); ++si) {
00261       converter.add_subset(GlobPattern(*si));
00262     }
00263   }
00264 
00265   if (!_excludes.empty()) {
00266     converter.clear_excludes();
00267     for (si = _excludes.begin(); si != _excludes.end(); ++si) {
00268       converter.add_exclude(GlobPattern(*si));
00269     }
00270   }
00271 
00272   if (!_ignore_sliders.empty()) {
00273     converter.clear_ignore_sliders();
00274     for (si = _ignore_sliders.begin(); si != _ignore_sliders.end(); ++si) {
00275       converter.add_ignore_slider(GlobPattern(*si));
00276     }
00277   }
00278 
00279   if (!_force_joints.empty()) {
00280     converter.clear_force_joints();
00281     for (si = _force_joints.begin(); si != _force_joints.end(); ++si) {
00282       converter.add_force_joint(GlobPattern(*si));
00283     }
00284   }
00285 
00286   // Copy in the path and animation parameters.
00287   apply_parameters(converter);
00288 
00289   // Set the coordinate system to match Maya's.
00290   if (!_got_coordinate_system) {
00291     _coordinate_system = converter._maya->get_coordinate_system();
00292   }
00293   _data->set_coordinate_system(_coordinate_system);
00294 
00295   converter.set_egg_data(_data);
00296 
00297   if (!converter.convert_file(_input_filename)) {
00298     nout << "Errors in conversion.\n";
00299     exit(1);
00300   }
00301 
00302   // Use the standard Maya units, if the user didn't specify
00303   // otherwise.  This always returns centimeters, which is the way all
00304   // Maya files are stored internally (and is the units returned by
00305   // all of the API functions called here).
00306   if (_input_units == DU_invalid) {
00307     _input_units = converter.get_input_units();
00308   }
00309 
00310   // Add the command line comment at the top of the egg file
00311   append_command_comment(_data);
00312 
00313   write_egg_file();
00314   
00315   // Clean and out
00316   close_output();
00317   _verbose = 0;
00318   _polygon_tolerance = 0.01;
00319   _polygon_output = 0;
00320   _transform_type = MayaToEggConverter::TT_model;
00321   _subsets.clear();
00322   _subroots.clear();
00323   _input_units = DU_invalid;
00324   _output_units = DU_invalid;
00325   _excludes.clear();
00326   _ignore_sliders.clear();
00327   _force_joints.clear();
00328   _got_transform = false;
00329   _transform = LMatrix4d::ident_mat();
00330   _normals_mode = NM_preserve;
00331   _normals_threshold = 0.0;
00332   _got_start_frame = false;
00333   _got_end_frame = false;
00334   _got_frame_inc = false;
00335   _got_neutral_frame = false;
00336   _got_input_frame_rate = false;
00337   _got_output_frame_rate = false;
00338   _got_output_filename = false;
00339   _merge_externals = false;
00340   _got_tbnall = false;
00341   _got_tbnauto = false;
00342   _got_transform = false;
00343   _coordinate_system = CS_yup_right;
00344   _noabs = false;
00345   _program_args.clear();
00346   _data->clear();
00347   _animation_convert = AC_none;
00348   _character_name = "";
00349   dummy->clear();
00350 }
00351 
00352 ////////////////////////////////////////////////////////////////////
00353 //     Function: MayaToEggServer::dispatch_transform_type
00354 //       Access: Protected, Static
00355 //  Description: Dispatches a parameter that expects a
00356 //               MayaToEggConverter::TransformType option.
00357 ////////////////////////////////////////////////////////////////////
00358 bool MayaToEggServer::
00359 dispatch_transform_type(const string &opt, const string &arg, void *var) {
00360   MayaToEggConverter::TransformType *ip = (MayaToEggConverter::TransformType *)var;
00361   (*ip) = MayaToEggConverter::string_transform_type(arg);
00362 
00363   if ((*ip) == MayaToEggConverter::TT_invalid) {
00364     nout << "Invalid type for -" << opt << ": " << arg << "\n"
00365          << "Valid types are all, model, dcs, and none.\n";
00366     return false;
00367   }
00368 
00369   return true;
00370 }
00371 
00372 ////////////////////////////////////////////////////////////////////
00373 //     Function: MayaToEggServer::poll
00374 //       Access: Public
00375 //  Description: Checks for any network activity and handles it, if
00376 //               appropriate, and then returns.  This must be called
00377 //               periodically
00378 ////////////////////////////////////////////////////////////////////
00379 void MayaToEggServer::
00380 poll() {
00381   // Listen for new connections
00382   qListener->poll();
00383 
00384   // If we have a new connection from a client create a new connection
00385   // pointer and add it to the reader list
00386   if (qListener->new_connection_available()) {
00387     PT(Connection) con;
00388     PT(Connection) rv;
00389     NetAddress address;
00390     if (qListener->get_new_connection(rv, address, con)) {
00391       qReader->add_connection(con);
00392       _clients.insert(con);
00393     }
00394   }
00395 
00396   // Check for reset clients
00397   if (qManager->reset_connection_available()) {
00398     PT(Connection) connection;
00399     if (qManager->get_reset_connection(connection)) {
00400       _clients.erase(connection);
00401       qManager->close_connection(connection);
00402     }
00403   }
00404   
00405   // Poll the readers (created above) and if they have data process it
00406   qReader->poll();
00407   if (qReader->data_available()) {
00408     // Grab the incomming data and unpack it
00409     NetDatagram datagram;
00410     if (qReader->get_data(datagram)) {
00411       DatagramIterator data(datagram);
00412       // First data should be the "argc" (argument count) from the client
00413       int argc = data.get_uint8();
00414 
00415       // Now we have to get clever because the rest of the data comes as strings
00416       // and parse_command_line() expects arguments of the standard argc, argv*[]
00417       // variety.
00418       // First, we need a string vector to hold all the strings from the datagram.
00419       // We also need a char * array to keep track of all the pointers we're gonna
00420       // malloc.  Needed later for cleanup.
00421       vector_string vargv;
00422       vector<char *> buffers;
00423 
00424       // Get the strings from the datagram and put them into the string vector
00425       int i;
00426       for ( i = 0; i < argc; i++ ) {
00427         vargv.push_back(data.get_string());
00428       }
00429 
00430       // Last string is the current directory the client was run from. Not part of
00431       // the argument list, but we still need it
00432       string cwd = data.get_string();
00433 
00434       // We allocate some memory to hold the pointers to the pointers we're going to
00435       // pass in to parse_command_line().
00436       char ** cargv = (char**) malloc(sizeof(char**) * argc);
00437 
00438       // Loop through the string arguments we got from the datagram and convert
00439       // them to const char *'s.  parse_command_line() expects char *'s, so we have
00440       // to copy these const versions into fresh char *, since there is no casting
00441       // from const char * to char *.
00442       for ( i = 0; i < argc; i++) {
00443         // string to const char *
00444         const char * cptr = vargv[i].c_str();
00445         // memory allocated for a new char * of size of the string
00446         char * buffer = (char*) malloc(vargv[i].capacity()+1);
00447         // Copy the const char * to the char *
00448         strcpy(buffer, cptr);
00449         // put this into the arry we defined above.  This is what will eventually
00450         // be passed to parse_command_line()
00451         cargv[i] = buffer;
00452         // keep track of the pointers to the  allocated memory for cleanup later
00453         buffers.push_back(buffer);
00454       }
00455       // Change to the client's current dir
00456 #ifdef WIN64_VC
00457       _chdir(cwd.c_str());
00458 #else
00459       chdir(cwd.c_str());
00460 #endif
00461 
00462       // Pass in the 'new' argc and argv we got from the client
00463       this->parse_command_line(argc, cargv);
00464       // Actually run the damn thing
00465       this->run();
00466 
00467       // Cleanup
00468       // First, release the string vector
00469       vargv.clear();
00470       // No, iterate through the char * vector and cleanup the malloc'd
00471       // pointers
00472       vector<char *>::iterator vi;
00473       for ( vi = buffers.begin() ; vi != buffers.end(); vi++) {
00474         free(*vi);
00475       }
00476       // Clean up the malloc'd pointer pointer
00477       free(cargv);
00478     } // qReader->get_data
00479 
00480     Clients::iterator ci;
00481     for (ci = _clients.begin(); ci != _clients.end(); ++ci) {
00482       qManager->close_connection(*ci);
00483     }
00484   } // qReader->data_available
00485 } // poll
00486 
00487 int main(int argc, char *argv[]) {
00488   // We don't want pystub on linux, since it gives problems with Maya's python.
00489 #ifdef _WIN32
00490   // A call to pystub() to force libpystub.so to be linked in.
00491   pystub();
00492 #endif
00493 
00494   MayaToEggServer prog;
00495   // Open a rendezvous port for receiving new connections from the client
00496   PT(Connection) rend = prog.qManager->open_TCP_server_rendezvous(4242, 50);
00497   if (rend.is_null()) {
00498     nout << "port opened fail";
00499   }
00500 
00501   // Add this connection to the listeners list
00502   prog.qListener->add_connection(rend);
00503 
00504   // Main loop.  Keep polling for connections, but don't eat up all the CPU.
00505   while (true) {
00506     prog.poll();
00507     Thread::force_yield();
00508   }
00509   return 0;
00510 }
00511 
 All Classes Functions Variables Enumerations