Panda3D
|
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