Panda3D
|
00001 // Filename: mayaToEggConverter.cxx 00002 // Created by: drose (10Nov99) 00003 // Modified 19Mar10 by ETC PandaSE team 00004 // Added set_vertex_color_modern to fix Phong shader bug; also see 00005 // header comment for mayaToEgg.cxx for more details 00006 // 00007 //////////////////////////////////////////////////////////////////// 00008 // 00009 // PANDA 3D SOFTWARE 00010 // Copyright (c) Carnegie Mellon University. All rights reserved. 00011 // 00012 // All use of this software is subject to the terms of the revised BSD 00013 // license. You should have received a copy of this license along 00014 // with this source code in a file named "LICENSE." 00015 // 00016 //////////////////////////////////////////////////////////////////// 00017 00018 #include "mayaToEggConverter.h" 00019 #include "mayaShader.h" 00020 #include "maya_funcs.h" 00021 #include "config_mayaegg.h" 00022 #include "mayaEggGroupUserData.h" 00023 00024 #include "eggData.h" 00025 #include "eggGroup.h" 00026 #include "eggTable.h" 00027 #include "eggVertex.h" 00028 #include "eggVertexPool.h" 00029 #include "eggNurbsSurface.h" 00030 #include "eggNurbsCurve.h" 00031 #include "eggPolygon.h" 00032 #include "eggPrimitive.h" 00033 #include "eggTexture.h" 00034 #include "eggTextureCollection.h" 00035 #include "eggXfmSAnim.h" 00036 #include "eggSAnimData.h" 00037 #include "string_utils.h" 00038 #include "dcast.h" 00039 00040 #include "pre_maya_include.h" 00041 #include <maya/MArgList.h> 00042 #include <maya/MColor.h> 00043 #include <maya/MDagPath.h> 00044 #include <maya/MFnCamera.h> 00045 #include <maya/MFnDagNode.h> 00046 #include <maya/MFnTransform.h> 00047 #include <maya/MFnLight.h> 00048 #include <maya/MFnNurbsSurface.h> 00049 #include <maya/MFnNurbsCurve.h> 00050 #include <maya/MFnMesh.h> 00051 #include <maya/MFnMeshData.h> 00052 #include <maya/MFnPlugin.h> 00053 #include <maya/MItDag.h> 00054 #include <maya/MLibrary.h> 00055 #include <maya/MMatrix.h> 00056 #include <maya/MObject.h> 00057 #include <maya/MPoint.h> 00058 #include <maya/MPointArray.h> 00059 #include <maya/MDoubleArray.h> 00060 #include <maya/MIntArray.h> 00061 #include <maya/MPxCommand.h> 00062 #include <maya/MStatus.h> 00063 #include <maya/MString.h> 00064 #include <maya/MTransformationMatrix.h> 00065 #include <maya/MVector.h> 00066 #include <maya/MTesselationParams.h> 00067 #include <maya/MAnimControl.h> 00068 #include <maya/MGlobal.h> 00069 #include <maya/MAnimUtil.h> 00070 #include <maya/MFnSkinCluster.h> 00071 #include <maya/MFnWeightGeometryFilter.h> 00072 #include <maya/MFnIkJoint.h> 00073 #include <maya/MFnSingleIndexedComponent.h> 00074 #include <maya/MFnDoubleIndexedComponent.h> 00075 #include <maya/MFnBlendShapeDeformer.h> 00076 #include <maya/MItDependencyGraph.h> 00077 #include <maya/MDagPathArray.h> 00078 #include <maya/MSelectionList.h> 00079 #include "post_maya_include.h" 00080 00081 00082 //////////////////////////////////////////////////////////////////// 00083 // Function: MayaToEggConverter::Constructor 00084 // Access: Public 00085 // Description: 00086 //////////////////////////////////////////////////////////////////// 00087 MayaToEggConverter:: 00088 MayaToEggConverter(const string &program_name) : 00089 _program_name(program_name), 00090 _tree(this) 00091 { 00092 // Make sure the library is properly initialized. 00093 init_libmayaegg(); 00094 00095 _from_selection = false; 00096 00097 _polygon_output = false; 00098 _polygon_tolerance = 0.01; 00099 _respect_maya_double_sided = maya_default_double_sided; 00100 _always_show_vertex_color = maya_default_vertex_color; 00101 _keep_all_uvsets = false; 00102 _round_uvs = false; 00103 _texture_copy = false; 00104 _legacy_shader = false; 00105 00106 _transform_type = TT_model; 00107 } 00108 00109 //////////////////////////////////////////////////////////////////// 00110 // Function: MayaToEggConverter::Copy Constructor 00111 // Access: Public 00112 // Description: 00113 //////////////////////////////////////////////////////////////////// 00114 MayaToEggConverter:: 00115 MayaToEggConverter(const MayaToEggConverter ©) : 00116 _program_name(copy._program_name), 00117 _from_selection(copy._from_selection), 00118 _subsets(copy._subsets), 00119 _subroots(copy._subroots), 00120 _excludes(copy._excludes), 00121 _ignore_sliders(copy._ignore_sliders), 00122 _force_joints(copy._force_joints), 00123 _tree(this), 00124 _maya(copy._maya), 00125 _polygon_output(copy._polygon_output), 00126 _polygon_tolerance(copy._polygon_tolerance), 00127 _respect_maya_double_sided(copy._respect_maya_double_sided), 00128 _always_show_vertex_color(copy._always_show_vertex_color), 00129 _keep_all_uvsets(copy._keep_all_uvsets), 00130 _round_uvs(copy._round_uvs), 00131 _texture_copy(copy._texture_copy), 00132 _legacy_shader(copy._legacy_shader), 00133 _transform_type(copy._transform_type) 00134 { 00135 } 00136 00137 //////////////////////////////////////////////////////////////////// 00138 // Function: MayaToEggConverter::Destructor 00139 // Access: Public, Virtual 00140 // Description: 00141 //////////////////////////////////////////////////////////////////// 00142 MayaToEggConverter:: 00143 ~MayaToEggConverter() { 00144 close_api(); 00145 } 00146 00147 //////////////////////////////////////////////////////////////////// 00148 // Function: MayaToEggConverter::make_copy 00149 // Access: Public, Virtual 00150 // Description: Allocates and returns a new copy of the converter. 00151 //////////////////////////////////////////////////////////////////// 00152 SomethingToEggConverter *MayaToEggConverter:: 00153 make_copy() { 00154 return new MayaToEggConverter(*this); 00155 } 00156 00157 //////////////////////////////////////////////////////////////////// 00158 // Function: MayaToEggConverter::get_name 00159 // Access: Public, Virtual 00160 // Description: Returns the English name of the file type this 00161 // converter supports. 00162 //////////////////////////////////////////////////////////////////// 00163 string MayaToEggConverter:: 00164 get_name() const { 00165 return "Maya"; 00166 } 00167 00168 //////////////////////////////////////////////////////////////////// 00169 // Function: MayaToEggConverter::get_extension 00170 // Access: Public, Virtual 00171 // Description: Returns the common extension of the file type this 00172 // converter supports. 00173 //////////////////////////////////////////////////////////////////// 00174 string MayaToEggConverter:: 00175 get_extension() const { 00176 return "mb"; 00177 } 00178 00179 //////////////////////////////////////////////////////////////////// 00180 // Function: MayaToEggConverter::get_additional_extensions 00181 // Access: Public, Virtual 00182 // Description: Returns a space-separated list of extension, in 00183 // addition to the one returned by get_extension(), that 00184 // are recognized by this converter. 00185 //////////////////////////////////////////////////////////////////// 00186 string MayaToEggConverter:: 00187 get_additional_extensions() const { 00188 return "ma"; 00189 } 00190 00191 //////////////////////////////////////////////////////////////////// 00192 // Function: MayaToEggConverter::convert_file 00193 // Access: Public, Virtual 00194 // Description: Handles the reading of the input file and converting 00195 // it to egg. Returns true if successful, false 00196 // otherwise. 00197 // 00198 // This is designed to be as generic as possible, 00199 // generally in support of run-time loading. 00200 // Also see convert_maya(). 00201 //////////////////////////////////////////////////////////////////// 00202 bool MayaToEggConverter:: 00203 convert_file(const Filename &filename) { 00204 if (!open_api()) { 00205 mayaegg_cat.error() 00206 << "Maya is not available.\n"; 00207 return false; 00208 } 00209 00210 // We must ensure our Maya pointers are cleared before we reset the 00211 // Maya scene, because resetting the Maya scene will invalidate all 00212 // the Maya pointers we are holding and cause a crash if we try to 00213 // free them later. 00214 clear(); 00215 00216 if (!_maya->read(filename)) { 00217 mayaegg_cat.error() 00218 << "Unable to read " << filename << "\n"; 00219 return false; 00220 } 00221 00222 if (_character_name.empty()) { 00223 _character_name = filename.get_basename_wo_extension(); 00224 } 00225 00226 return convert_maya(); 00227 } 00228 00229 //////////////////////////////////////////////////////////////////// 00230 // Function: MayaToEggConverter::clear_subroots 00231 // Access: Public 00232 // Description: Empties the list of subroot nodes added via 00233 // add_subroot(). The entire file will once again be 00234 // converted. 00235 //////////////////////////////////////////////////////////////////// 00236 void MayaToEggConverter:: 00237 clear_subroots() { 00238 _subroots.clear(); 00239 } 00240 00241 //////////////////////////////////////////////////////////////////// 00242 // Function: MayaToEggConverter::add_subroot 00243 // Access: Public 00244 // Description: Adds a name pattern to the list of subroot nodes. If 00245 // the list of subroot nodes is not empty, then only a 00246 // subroot of the nodes in the maya file will be 00247 // converted: those whose names match one of the 00248 // patterns given on this list. 00249 //////////////////////////////////////////////////////////////////// 00250 void MayaToEggConverter:: 00251 add_subroot(const GlobPattern &glob) { 00252 _subroots.push_back(glob); 00253 } 00254 00255 //////////////////////////////////////////////////////////////////// 00256 // Function: MayaToEggConverter::clear_subsets 00257 // Access: Public 00258 // Description: Empties the list of subset nodes added via 00259 // add_subset(). The entire file will once again be 00260 // converted. 00261 //////////////////////////////////////////////////////////////////// 00262 void MayaToEggConverter:: 00263 clear_subsets() { 00264 _subsets.clear(); 00265 } 00266 00267 //////////////////////////////////////////////////////////////////// 00268 // Function: MayaToEggConverter::add_subset 00269 // Access: Public 00270 // Description: Adds a name pattern to the list of subset nodes. If 00271 // the list of subset nodes is not empty, then only a 00272 // subset of the nodes in the maya file will be 00273 // converted: those whose names match one of the 00274 // patterns given on this list. 00275 //////////////////////////////////////////////////////////////////// 00276 void MayaToEggConverter:: 00277 add_subset(const GlobPattern &glob) { 00278 _subsets.push_back(glob); 00279 } 00280 00281 //////////////////////////////////////////////////////////////////// 00282 // Function: MayaToEggConverter::clear_excludes 00283 // Access: Public 00284 // Description: Empties the list of excluded nodes added via 00285 // add_exclude(). 00286 //////////////////////////////////////////////////////////////////// 00287 void MayaToEggConverter:: 00288 clear_excludes() { 00289 _excludes.clear(); 00290 } 00291 00292 //////////////////////////////////////////////////////////////////// 00293 // Function: MayaToEggConverter::add_exclude 00294 // Access: Public 00295 // Description: Adds a name pattern to the list of excluded nodes. 00296 //////////////////////////////////////////////////////////////////// 00297 void MayaToEggConverter:: 00298 add_exclude(const GlobPattern &glob) { 00299 _excludes.push_back(glob); 00300 } 00301 00302 //////////////////////////////////////////////////////////////////// 00303 // Function: MayaToEggConverter::clear_ignore_sliders 00304 // Access: Public 00305 // Description: Empties the list of ignore_sliders added via 00306 // add_ignore_slider(). No sliders will be ignored. 00307 //////////////////////////////////////////////////////////////////// 00308 void MayaToEggConverter:: 00309 clear_ignore_sliders() { 00310 _ignore_sliders.clear(); 00311 } 00312 00313 //////////////////////////////////////////////////////////////////// 00314 // Function: MayaToEggConverter::add_ignore_slider 00315 // Access: Public 00316 // Description: Adds a name pattern to the list of ignore_sliders. 00317 // Any slider (blend shape deformer) that matches a name 00318 // on the list will not be converted or otherwise 00319 // molested by the converter. This is occasionally 00320 // necessary to filter out automatically-created sliders 00321 // that are not intended to be used directly, but 00322 // instead have an indirect effect on other sliders. 00323 //////////////////////////////////////////////////////////////////// 00324 void MayaToEggConverter:: 00325 add_ignore_slider(const GlobPattern &glob) { 00326 _ignore_sliders.push_back(glob); 00327 } 00328 00329 //////////////////////////////////////////////////////////////////// 00330 // Function: MayaToEggConverter::ignore_slider 00331 // Access: Public 00332 // Description: Returns true if the indicated name is on the list of 00333 // sliders to ignore, false otherwise. 00334 //////////////////////////////////////////////////////////////////// 00335 bool MayaToEggConverter:: 00336 ignore_slider(const string &name) const { 00337 Globs::const_iterator gi; 00338 for (gi = _ignore_sliders.begin(); gi != _ignore_sliders.end(); ++gi) { 00339 if ((*gi).matches(name)) { 00340 return true; 00341 } 00342 } 00343 00344 return false; 00345 } 00346 00347 //////////////////////////////////////////////////////////////////// 00348 // Function: MayaToEggConverter::clear_force_joints 00349 // Access: Public 00350 // Description: Empties the list of force_joints added via 00351 // add_force_joint(). No joints will be forced. 00352 //////////////////////////////////////////////////////////////////// 00353 void MayaToEggConverter:: 00354 clear_force_joints() { 00355 _force_joints.clear(); 00356 } 00357 00358 //////////////////////////////////////////////////////////////////// 00359 // Function: MayaToEggConverter::add_force_joint 00360 // Access: Public 00361 // Description: Adds a name pattern to the list of force_joints. 00362 // 00363 // Any DAG node that matches a name on the list will be 00364 // treated as if it were a joint during the conversion 00365 // process; it will receive animation and position 00366 // information. Normally, a true Maya joint, as well as 00367 // any DAG nodes whose transforms are animated, will 00368 // automatically be flagged as a Panda joint. 00369 //////////////////////////////////////////////////////////////////// 00370 void MayaToEggConverter:: 00371 add_force_joint(const GlobPattern &glob) { 00372 _force_joints.push_back(glob); 00373 } 00374 00375 //////////////////////////////////////////////////////////////////// 00376 // Function: MayaToEggConverter::force_joint 00377 // Access: Public 00378 // Description: Returns true if the indicated name is on the list of 00379 // DAG nodes to treat as a joint, false otherwise. 00380 //////////////////////////////////////////////////////////////////// 00381 bool MayaToEggConverter:: 00382 force_joint(const string &name) const { 00383 Globs::const_iterator gi; 00384 for (gi = _force_joints.begin(); gi != _force_joints.end(); ++gi) { 00385 if ((*gi).matches(name)) { 00386 return true; 00387 } 00388 } 00389 00390 return false; 00391 } 00392 00393 //////////////////////////////////////////////////////////////////// 00394 // Function: MayaToEggConverter::set_from_selection 00395 // Access: Public 00396 // Description: Sets the flag that indicates whether the currently 00397 // selected Maya geometry will be converted. If this is 00398 // true, and the selection is nonempty, then only the 00399 // selected geometry will be converted. If this is 00400 // false, the entire file will be converted. 00401 //////////////////////////////////////////////////////////////////// 00402 void MayaToEggConverter:: 00403 set_from_selection(bool from_selection) { 00404 _from_selection = from_selection; 00405 } 00406 00407 //////////////////////////////////////////////////////////////////// 00408 // Function: MayaToEggConverter::get_input_units 00409 // Access: Public, Virtual 00410 // Description: This may be called after convert_file() has been 00411 // called and returned true, indicating a successful 00412 // conversion. It will return the distance units 00413 // represented by the converted egg file, if known, or 00414 // DU_invalid if not known. 00415 //////////////////////////////////////////////////////////////////// 00416 DistanceUnit MayaToEggConverter:: 00417 get_input_units() { 00418 return _maya->get_units(); 00419 } 00420 00421 //////////////////////////////////////////////////////////////////// 00422 // Function: MayaToEggConverter::convert_maya 00423 // Access: Public 00424 // Description: Fills up the egg_data structure according to the 00425 // global maya model data. Returns true if successful, 00426 // false if there is an error. 00427 //////////////////////////////////////////////////////////////////// 00428 bool MayaToEggConverter:: 00429 convert_maya() { 00430 clear(); 00431 clear_error(); 00432 00433 if (!open_api()) { 00434 mayaegg_cat.error() 00435 << "Maya is not available.\n"; 00436 return false; 00437 } 00438 00439 if (_egg_data->get_coordinate_system() == CS_default) { 00440 _egg_data->set_coordinate_system(_maya->get_coordinate_system()); 00441 } 00442 00443 mayaegg_cat.info() 00444 << "Converting from Maya.\n"; 00445 00446 // Figure out the animation parameters. 00447 double start_frame, end_frame, frame_inc, input_frame_rate, output_frame_rate; 00448 if (has_start_frame()) { 00449 start_frame = get_start_frame(); 00450 } else { 00451 start_frame = MAnimControl::minTime().value(); 00452 } 00453 if (has_end_frame()) { 00454 end_frame = get_end_frame(); 00455 } else { 00456 end_frame = MAnimControl::maxTime().value(); 00457 //end_frame = MAnimControl::animationEndTime().value(); //masad: we could use this 00458 } 00459 if (has_frame_inc()) { 00460 frame_inc = get_frame_inc(); 00461 } else { 00462 frame_inc = 1.0; 00463 } 00464 if (has_input_frame_rate()) { 00465 input_frame_rate = get_input_frame_rate(); 00466 } else { 00467 MTime time(1.0, MTime::kSeconds); 00468 input_frame_rate = time.as(MTime::uiUnit()); 00469 } 00470 if (has_output_frame_rate()) { 00471 output_frame_rate = get_output_frame_rate(); 00472 } else { 00473 output_frame_rate = input_frame_rate; 00474 } 00475 00476 frame_inc = frame_inc * input_frame_rate / output_frame_rate; 00477 00478 bool all_ok = _tree.build_hierarchy(); 00479 00480 if (all_ok) { 00481 if (!_subroots.empty()) { 00482 Globs::const_iterator gi; 00483 for (gi = _subroots.begin(); gi != _subroots.end(); ++gi) { 00484 if (!_tree.tag_joint_named(*gi)) { 00485 mayaegg_cat.info() 00486 << "No node matching " << *gi << " found.\n"; 00487 } 00488 } 00489 00490 } else { 00491 // This call makes every node a potential joint; but it does not 00492 // necessarily force nodes to be joints. 00493 _tree.tag_joint_all(); 00494 } 00495 } 00496 00497 if (all_ok) { 00498 if (_from_selection) { 00499 all_ok = _tree.tag_selected(); 00500 } else if (!_subsets.empty()) { 00501 Globs::const_iterator gi; 00502 for (gi = _subsets.begin(); gi != _subsets.end(); ++gi) { 00503 if (!_tree.tag_named(*gi)) { 00504 mayaegg_cat.info() 00505 << "No node matching " << *gi << " found.\n"; 00506 } 00507 } 00508 00509 } else { 00510 _tree.tag_all(); 00511 } 00512 } 00513 00514 if (all_ok) { 00515 if (!_excludes.empty()) { 00516 Globs::const_iterator gi; 00517 for (gi = _excludes.begin(); gi != _excludes.end(); ++gi) { 00518 if (!_tree.untag_named(*gi)) { 00519 mayaegg_cat.info() 00520 << "No node matching " << *gi << " found.\n"; 00521 } 00522 } 00523 } 00524 } 00525 00526 if (all_ok) { 00527 switch (get_animation_convert()) { 00528 case AC_pose: 00529 // pose: set to a specific frame, then get out the static geometry. 00530 mayaegg_cat.info(false) 00531 << "frame " << start_frame << "\n"; 00532 MGlobal::viewFrame(MTime(start_frame, MTime::uiUnit())); 00533 // fall through 00534 00535 case AC_none: 00536 // none: just get out a static model, no animation. 00537 mayaegg_cat.info() << "ac_none" << endl; 00538 all_ok = convert_hierarchy(get_egg_data()); 00539 break; 00540 00541 case AC_flip: 00542 case AC_strobe: 00543 // flip or strobe: get out a series of static models, one per 00544 // frame, under a sequence node for AC_flip. 00545 all_ok = convert_flip(start_frame, end_frame, frame_inc, 00546 output_frame_rate); 00547 break; 00548 00549 case AC_model: 00550 // model: get out an animatable model with joints and vertex 00551 // membership. 00552 all_ok = convert_char_model(); 00553 break; 00554 00555 case AC_chan: 00556 // chan: get out a series of animation tables. 00557 all_ok = convert_char_chan(start_frame, end_frame, frame_inc, 00558 output_frame_rate); 00559 break; 00560 00561 case AC_both: 00562 // both: Put a model and its animation into the same egg file. 00563 _animation_convert = AC_model; 00564 if (!convert_char_model()) { 00565 all_ok = false; 00566 } 00567 _animation_convert = AC_chan; 00568 if (!convert_char_chan(start_frame, end_frame, frame_inc, 00569 output_frame_rate)) { 00570 all_ok = false; 00571 } 00572 break; 00573 00574 case AC_invalid: 00575 break; 00576 }; 00577 00578 reparent_decals(get_egg_data()); 00579 } 00580 00581 if (had_error()) { 00582 all_ok = false; 00583 } 00584 00585 if (all_ok) { 00586 mayaegg_cat.info() 00587 << "Converted, no errors.\n"; 00588 } else { 00589 mayaegg_cat.info() 00590 << "Errors encountered in conversion.\n"; 00591 } 00592 00593 return all_ok; 00594 } 00595 00596 //////////////////////////////////////////////////////////////////// 00597 // Function: MayaToEggConverter::open_api 00598 // Access: Public 00599 // Description: Attempts to open the Maya API if it was not already 00600 // open, and returns true if successful, or false if 00601 // there is an error. 00602 //////////////////////////////////////////////////////////////////// 00603 bool MayaToEggConverter:: 00604 open_api(bool revert_directory) { 00605 00606 if (_maya == (MayaApi *)NULL || !_maya->is_valid()) { 00607 //maya to egg converter only needs a read license. 00608 //only egg2maya need write lisences. 00609 _maya = MayaApi::open_api(_program_name, true, revert_directory); 00610 } 00611 return _maya->is_valid(); 00612 } 00613 00614 //////////////////////////////////////////////////////////////////// 00615 // Function: MayaToEggConverter::close_api 00616 // Access: Public 00617 // Description: Closes the Maya API, if it was previously opened. 00618 // Caution! Maya appears to call exit() when its API is 00619 // closed. 00620 //////////////////////////////////////////////////////////////////// 00621 void MayaToEggConverter:: 00622 close_api() { 00623 // We have to clear the shaders, at least, before we release the 00624 // Maya API. 00625 clear(); 00626 _maya.clear(); 00627 } 00628 00629 //////////////////////////////////////////////////////////////////// 00630 // Function: MayaToEggConverter::clear 00631 // Access: Public 00632 // Description: Frees all of the Maya pointers kept within this 00633 // object, in preparation for loading a new scene or 00634 // releasing the Maya API. 00635 //////////////////////////////////////////////////////////////////// 00636 void MayaToEggConverter:: 00637 clear() { 00638 _tree.clear(); 00639 _textures.clear(); 00640 _shaders.clear(); 00641 } 00642 00643 //////////////////////////////////////////////////////////////////// 00644 // Function: MayaToEggConverter::convert_flip 00645 // Access: Private 00646 // Description: Converts the animation as a series of models that 00647 // cycle (flip) from one to the next at the appropriate 00648 // frame rate. This is the most likely to convert 00649 // precisely (since we ask Maya to tell us the vertex 00650 // position each time) but it is the most wasteful in 00651 // terms of memory utilization (since a complete of the 00652 // model is stored for each frame). 00653 //////////////////////////////////////////////////////////////////// 00654 bool MayaToEggConverter:: 00655 convert_flip(double start_frame, double end_frame, double frame_inc, 00656 double output_frame_rate) { 00657 bool all_ok = true; 00658 00659 EggGroup *sequence_node = new EggGroup(_character_name); 00660 get_egg_data()->add_child(sequence_node); 00661 if (_animation_convert == AC_flip) { 00662 sequence_node->set_switch_flag(true); 00663 sequence_node->set_switch_fps(output_frame_rate); 00664 } 00665 00666 MTime frame(start_frame, MTime::uiUnit()); 00667 MTime frame_stop(end_frame, MTime::uiUnit()); 00668 while (frame <= frame_stop) { 00669 mayaegg_cat.info(false) 00670 << "frame " << frame.value() << "\n"; 00671 ostringstream name_strm; 00672 name_strm << "frame" << frame.value(); 00673 EggGroup *frame_root = new EggGroup(name_strm.str()); 00674 sequence_node->add_child(frame_root); 00675 00676 MGlobal::viewFrame(frame); 00677 if (!convert_hierarchy(frame_root)) { 00678 all_ok = false; 00679 } 00680 00681 frame += frame_inc; 00682 } 00683 00684 return all_ok; 00685 } 00686 00687 //////////////////////////////////////////////////////////////////// 00688 // Function: MayaToEggConverter::convert_char_model 00689 // Access: Private 00690 // Description: Converts the file as an animatable character 00691 // model, with joints and vertex membership. 00692 //////////////////////////////////////////////////////////////////// 00693 bool MayaToEggConverter:: 00694 convert_char_model() { 00695 if (has_neutral_frame()) { 00696 MTime frame(get_neutral_frame(), MTime::uiUnit()); 00697 mayaegg_cat.info(false) 00698 << "neutral frame " << frame.value() << "\n"; 00699 MGlobal::viewFrame(frame); 00700 } 00701 00702 // It's also important for us to reset all the blend shape sliders 00703 // to 0 before we get out the model. Otherwise, the model we 00704 // convert will have the current positions of the sliders baked in. 00705 _tree.reset_sliders(); 00706 00707 EggGroup *char_node = new EggGroup(_character_name); 00708 get_egg_data()->add_child(char_node); 00709 char_node->set_dart_type(EggGroup::DT_default); 00710 00711 return convert_hierarchy(char_node); 00712 } 00713 00714 //////////////////////////////////////////////////////////////////// 00715 // Function: MayaToEggConverter::convert_char_chan 00716 // Access: Private 00717 // Description: Converts the animation as a series of tables to apply 00718 // to the character model, as retrieved earlier via 00719 // AC_model. 00720 //////////////////////////////////////////////////////////////////// 00721 bool MayaToEggConverter:: 00722 convert_char_chan(double start_frame, double end_frame, double frame_inc, 00723 double output_frame_rate) { 00724 // MStatus status; 00725 00726 EggTable *root_table_node = new EggTable(); 00727 get_egg_data()->add_child(root_table_node); 00728 EggTable *bundle_node = new EggTable(_character_name); 00729 bundle_node->set_table_type(EggTable::TT_bundle); 00730 root_table_node->add_child(bundle_node); 00731 EggTable *skeleton_node = new EggTable("<skeleton>"); 00732 bundle_node->add_child(skeleton_node); 00733 EggTable *morph_node = new EggTable("morph"); 00734 bundle_node->add_child(morph_node); 00735 00736 // Set the frame rate before we start asking for anim tables to be 00737 // created. 00738 _tree._fps = output_frame_rate; 00739 _tree.clear_egg(get_egg_data(), NULL, skeleton_node, morph_node); 00740 00741 // Now we can get the animation data by walking through all of the 00742 // frames, one at a time, and getting the joint angles at each 00743 // frame. 00744 00745 // This is just a temporary EggGroup to receive the transform for 00746 // each joint each frame. 00747 PT(EggGroup) tgroup = new EggGroup; 00748 00749 int num_nodes = _tree.get_num_nodes(); 00750 int num_sliders = _tree.get_num_blend_descs(); 00751 int i; 00752 00753 MTime frame(start_frame, MTime::uiUnit()); 00754 MTime frame_stop(end_frame, MTime::uiUnit()); 00755 while (frame <= frame_stop) { 00756 if (mayaegg_cat.is_debug()) { 00757 mayaegg_cat.debug(false) 00758 << "frame " << frame.value() << "\n"; 00759 } else { 00760 // We have to write to cerr instead of mayaegg_cat to allow 00761 // flushing without writing a newline. 00762 cerr << "." << flush; 00763 } 00764 MGlobal::viewFrame(frame); 00765 00766 for (i = 0; i < num_nodes; i++) { 00767 MayaNodeDesc *node_desc = _tree.get_node(i); 00768 if (node_desc->is_joint()) { 00769 if (mayaegg_cat.is_spam()) { 00770 mayaegg_cat.spam() 00771 << "joint " << node_desc->get_name() << "\n"; 00772 } 00773 get_joint_transform(node_desc->get_dag_path(), tgroup); 00774 EggXfmSAnim *anim = _tree.get_egg_anim(node_desc); 00775 if (!anim->add_data(tgroup->get_transform3d())) { 00776 mayaegg_cat.error() 00777 << "Invalid transform on " << node_desc->get_name() 00778 << " frame " << frame.value() << ".\n"; 00779 } 00780 } 00781 } 00782 00783 for (i = 0; i < num_sliders; i++) { 00784 MayaBlendDesc *blend_desc = _tree.get_blend_desc(i); 00785 if (mayaegg_cat.is_spam()) { 00786 mayaegg_cat.spam() 00787 << "slider " << blend_desc->get_name() << "\n"; 00788 } 00789 EggSAnimData *anim = _tree.get_egg_slider(blend_desc); 00790 anim->add_data(blend_desc->get_slider()); 00791 } 00792 00793 frame += frame_inc; 00794 } 00795 00796 // Now optimize all of the tables we just filled up, for no real 00797 // good reason, except that it makes the resulting egg file a little 00798 // easier to read. 00799 for (i = 0; i < num_nodes; i++) { 00800 MayaNodeDesc *node_desc = _tree.get_node(i); 00801 if (node_desc->is_joint()) { 00802 _tree.get_egg_anim(node_desc)->optimize(); 00803 } 00804 } 00805 00806 for (i = 0; i < num_sliders; i++) { 00807 MayaBlendDesc *blend_desc = _tree.get_blend_desc(i); 00808 EggSAnimData *anim = _tree.get_egg_slider(blend_desc); 00809 anim->optimize(); 00810 } 00811 00812 mayaegg_cat.info(false) 00813 << "\n"; 00814 00815 return true; 00816 } 00817 00818 //////////////////////////////////////////////////////////////////// 00819 // Function: MayaToEggConverter::convert_hierarchy 00820 // Access: Private 00821 // Description: Generates egg structures for each node in the Maya 00822 // hierarchy. 00823 //////////////////////////////////////////////////////////////////// 00824 bool MayaToEggConverter:: 00825 convert_hierarchy(EggGroupNode *egg_root) { 00826 int num_nodes = _tree.get_num_nodes(); 00827 00828 if (_round_uvs) { 00829 mayaegg_cat.info() << "will round up uv coordinates" << endl; 00830 } 00831 00832 if (_keep_all_uvsets) { 00833 mayaegg_cat.info() << "will keep_all_uvsets" << endl; 00834 } 00835 // give some feedback about whether special options are on 00836 if (_texture_copy) { 00837 mayaegg_cat.info() << "will copy textures to" <<_texture_out_dir<< endl; 00838 } 00839 if (_legacy_shader) { 00840 mayaegg_cat.info() << "will disable modern Phong shader path. using legacy" << endl; 00841 } 00842 _tree.clear_egg(get_egg_data(), egg_root, NULL, NULL); 00843 for (int i = 0; i < num_nodes; i++) { 00844 MayaNodeDesc *node = _tree.get_node(i); 00845 if (!process_model_node(node)) { 00846 return false; 00847 } 00848 } 00849 return true; 00850 } 00851 00852 //////////////////////////////////////////////////////////////////// 00853 // Function: MayaToEggConverter::process_model_node 00854 // Access: Private 00855 // Description: Converts the indicated Maya node (given a MDagPath, 00856 // similar in concept to Panda's NodePath) to the 00857 // corresponding Egg structure. Returns true if 00858 // successful, false if an error was encountered. 00859 //////////////////////////////////////////////////////////////////// 00860 bool MayaToEggConverter:: 00861 process_model_node(MayaNodeDesc *node_desc) { 00862 if (!node_desc->has_dag_path()) { 00863 // If the node has no Maya equivalent, never mind. 00864 return true; 00865 } 00866 00867 MDagPath dag_path = node_desc->get_dag_path(); 00868 00869 MStatus status; 00870 MFnDagNode dag_node(dag_path, &status); 00871 if (!status) { 00872 status.perror("MFnDagNode constructor"); 00873 mayaegg_cat.error() << dag_path.fullPathName().asChar() << "\n"; 00874 return false; 00875 } 00876 00877 MObject node = dag_path.transform(&status); 00878 if (!status) { 00879 status.perror("dag_path.transform()"); 00880 return false; 00881 } 00882 00883 string path = dag_path.fullPathName().asChar(); 00884 00885 if (mayaegg_cat.is_debug()) { 00886 mayaegg_cat.debug() 00887 << path << ": " << dag_node.typeName().asChar(); 00888 00889 if (MAnimUtil::isAnimated(dag_path)) { 00890 mayaegg_cat.debug(false) 00891 << " (animated)"; 00892 } 00893 00894 mayaegg_cat.debug(false) << "\n"; 00895 } 00896 00897 if (dag_node.inUnderWorld()) { 00898 if (mayaegg_cat.is_debug()) { 00899 mayaegg_cat.debug() 00900 << "Ignoring underworld node " << path 00901 << "\n"; 00902 } 00903 00904 } else if (dag_node.isIntermediateObject()) { 00905 if (mayaegg_cat.is_debug()) { 00906 mayaegg_cat.debug() 00907 << "Ignoring intermediate object " << path 00908 << "\n"; 00909 } 00910 00911 } else if (dag_path.hasFn(MFn::kCamera)) { 00912 if (mayaegg_cat.is_debug()) { 00913 mayaegg_cat.debug() 00914 << "Ignoring camera node " << path 00915 << "\n"; 00916 } 00917 00918 MFnCamera camera (dag_path, &status); 00919 if ( !status ) { 00920 status.perror("MFnCamera constructor"); 00921 mayaegg_cat.error() << "camera extraction failed" << endl; 00922 return false; 00923 } 00924 00925 // Extract some interesting Camera data 00926 if (mayaegg_cat.is_spam()) { 00927 MPoint eyePoint = camera.eyePoint(MSpace::kWorld); 00928 mayaegg_cat.spam() << " eyePoint: " << eyePoint.x << " " 00929 << eyePoint.y << " " << eyePoint.z << endl; 00930 mayaegg_cat.spam() << " upDirection: " 00931 << camera.upDirection(MSpace::kWorld) << endl; 00932 mayaegg_cat.spam() << " viewDirection: " 00933 << camera.viewDirection(MSpace::kWorld) << endl; 00934 mayaegg_cat.spam() << " aspectRatio: " << camera.aspectRatio() << endl; 00935 mayaegg_cat.spam() << " horizontalFilmAperture: " 00936 << camera.horizontalFilmAperture() << endl; 00937 mayaegg_cat.spam() << " verticalFilmAperture: " 00938 << camera.verticalFilmAperture() << endl; 00939 } 00940 00941 } else if (dag_path.hasFn(MFn::kLight)) { 00942 if (mayaegg_cat.is_debug()) { 00943 mayaegg_cat.debug() 00944 << "Ignoring light node " << path 00945 << "\n"; 00946 } 00947 /* 00948 00949 MFnLight light (dag_path, &status); 00950 if ( !status ) { 00951 status.perror("MFnLight constructor"); 00952 mayaegg_cat.error() << "light extraction failed" << endl; 00953 return false; 00954 } 00955 00956 // Get the translation/rotation/scale data 00957 //printTransformData(dag_path, quiet); 00958 00959 // Extract some interesting Light data 00960 MColor color; 00961 00962 color = light.color(); 00963 cout << " color: [" 00964 << color.r << ", " 00965 << color.g << ", " 00966 << color.b << "]\n"; 00967 color = light.shadowColor(); 00968 cout << " shadowColor: [" 00969 << color.r << ", " 00970 << color.g << ", " 00971 << color.b << "]\n"; 00972 00973 cout << " intensity: " << light.intensity() << endl; 00974 */ 00975 } else if (dag_path.hasFn(MFn::kNurbsSurface)) { 00976 EggGroup *egg_group = _tree.get_egg_group(node_desc); 00977 get_transform(node_desc, dag_path, egg_group); 00978 00979 if (node_desc->is_tagged()) { 00980 MFnNurbsSurface surface(dag_path, &status); 00981 if (!status) { 00982 mayaegg_cat.info() 00983 << "Error in node " << path 00984 << ":\n" 00985 << " it appears to have a NURBS surface, but does not.\n"; 00986 } else { 00987 make_nurbs_surface(node_desc, dag_path, surface, egg_group); 00988 } 00989 } 00990 } else if (dag_path.hasFn(MFn::kNurbsCurve)) { 00991 // Only convert NurbsCurves if we aren't making an animated model. 00992 // Animated models, as a general rule, don't want these sorts of 00993 // things in them. 00994 if (_animation_convert != AC_model) { 00995 EggGroup *egg_group = _tree.get_egg_group(node_desc); 00996 get_transform(node_desc, dag_path, egg_group); 00997 00998 if (node_desc->is_tagged()) { 00999 MFnNurbsCurve curve(dag_path, &status); 01000 if (!status) { 01001 mayaegg_cat.info() 01002 << "Error in node " << path << ":\n" 01003 << " it appears to have a NURBS curve, but does not.\n"; 01004 } else { 01005 make_nurbs_curve(dag_path, curve, egg_group); 01006 } 01007 } 01008 } 01009 01010 } else if (dag_path.hasFn(MFn::kMesh)) { 01011 if (node_desc->is_tagged()) { 01012 EggGroup *egg_group = _tree.get_egg_group(node_desc); 01013 get_transform(node_desc, dag_path, egg_group); 01014 MFnMesh mesh(dag_path, &status); 01015 if (!status) { 01016 mayaegg_cat.info() 01017 << "Error in node " << path << ":\n" 01018 << " it appears to have a polygon mesh, but does not.\n"; 01019 } else { 01020 make_polyset(node_desc, dag_path, mesh, egg_group); 01021 } 01022 } 01023 /* 01024 EggGroup *egg_group = _tree.get_egg_group(node_desc); 01025 get_transform(node_desc, dag_path, egg_group); 01026 01027 if (node_desc->is_tagged()) { 01028 MFnMesh mesh(dag_path, &status); 01029 if (!status) { 01030 mayaegg_cat.info() 01031 << "Error in node " << path << ":\n" 01032 << " it appears to have a polygon mesh, but does not.\n"; 01033 } else { 01034 make_polyset(node_desc, dag_path, mesh, egg_group); 01035 } 01036 } 01037 */ 01038 } else if (dag_path.hasFn(MFn::kLocator)) { 01039 if (_animation_convert == AC_none) { 01040 if (!node_desc->is_tagged()) { 01041 return true; 01042 } 01043 } 01044 EggGroup *egg_group = _tree.get_egg_group(node_desc); 01045 01046 if (mayaegg_cat.is_debug()) { 01047 mayaegg_cat.debug() 01048 << "Locator at " << path << "\n"; 01049 } 01050 01051 if (node_desc->is_tagged()) { 01052 // Presumably, the locator's position has some meaning to the 01053 // end-user, so we will implicitly tag it with the DCS flag so it 01054 // won't get flattened out. 01055 if (_animation_convert != AC_model) { 01056 // For now, don't set the DCS flag on locators within 01057 // character models, since egg-optchar doesn't understand 01058 // this. Perhaps there's no reason to ever change this, since 01059 // locators within character models may not be meaningful. 01060 egg_group->set_dcs_type(EggGroup::DC_net); 01061 } 01062 get_transform(node_desc, dag_path, egg_group); 01063 make_locator(dag_path, dag_node, egg_group); 01064 } 01065 01066 } else { 01067 // Just a generic node. 01068 if (_animation_convert == AC_none) { 01069 if (!node_desc->is_tagged()) { 01070 return true; 01071 } 01072 } 01073 EggGroup *egg_group = _tree.get_egg_group(node_desc); 01074 get_transform(node_desc, dag_path, egg_group); 01075 } 01076 01077 return true; 01078 } 01079 01080 //////////////////////////////////////////////////////////////////// 01081 // Function: MayaToEggConverter::get_transform 01082 // Access: Private 01083 // Description: Extracts the transform on the indicated Maya node, 01084 // and applies it to the corresponding Egg node. 01085 //////////////////////////////////////////////////////////////////// 01086 void MayaToEggConverter:: 01087 get_transform(MayaNodeDesc *node_desc, const MDagPath &dag_path, 01088 EggGroup *egg_group) { 01089 if (_animation_convert == AC_model) { 01090 // When we're getting an animated model, we only get transforms 01091 // for joints, and they get converted in a special way. 01092 01093 if (node_desc->is_joint()) { 01094 if (mayaegg_cat.is_spam()) { 01095 mayaegg_cat.spam() 01096 << "gt: joint " << node_desc->get_name() << "\n"; 01097 } 01098 get_joint_transform(dag_path, egg_group); 01099 } 01100 return; 01101 } 01102 01103 MStatus status; 01104 MObject transformNode = dag_path.transform(&status); 01105 if (!status && status.statusCode() == MStatus::kInvalidParameter) { 01106 // This node has no transform - i.e., it's the world node 01107 return; 01108 } 01109 01110 // Billboards always get the transform set. 01111 if (egg_group->get_billboard_type() == EggGroup::BT_none) { 01112 switch (_transform_type) { 01113 case TT_all: 01114 break; 01115 01116 case TT_model: 01117 if (!egg_group->get_model_flag() && !egg_group->has_dcs_type()) { 01118 return; 01119 } 01120 break; 01121 01122 case TT_dcs: 01123 if (!egg_group->has_dcs_type()) { 01124 return; 01125 } 01126 break; 01127 01128 case TT_none: 01129 case TT_invalid: 01130 return; 01131 } 01132 } 01133 01134 // Extract the matrix from the dag path. 01135 MMatrix mat = dag_path.inclusiveMatrix(&status); 01136 if (!status) { 01137 status.perror("Can't get transform matrix"); 01138 return; 01139 } 01140 LMatrix4d m4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3], 01141 mat[1][0], mat[1][1], mat[1][2], mat[1][3], 01142 mat[2][0], mat[2][1], mat[2][2], mat[2][3], 01143 mat[3][0], mat[3][1], mat[3][2], mat[3][3]); 01144 01145 // Maya has a rotate pivot, separate from its transform. Usually we 01146 // care more about the rotate pivot than we do about the transform, 01147 // so get the rotate pivot too. 01148 MFnTransform transform(transformNode, &status); 01149 if (!status) { 01150 status.perror("MFnTransform constructor"); 01151 return; 01152 } 01153 MPoint pivot = transform.rotatePivot(MSpace::kObject, &status); 01154 if (!status) { 01155 status.perror("Can't get rotate pivot"); 01156 return; 01157 } 01158 01159 // We need to convert the pivot to world coordinates. (Maya can 01160 // only tell it to us in local coordinates.) 01161 LPoint3d p3d(pivot[0], pivot[1], pivot[2]); 01162 p3d = p3d * m4d; 01163 01164 // Now recenter the matrix about the pivot point. 01165 m4d.set_row(3, p3d); 01166 01167 // Convert the recentered matrix into the group's space and store 01168 // it. 01169 m4d = m4d * egg_group->get_node_frame_inv(); 01170 if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) { 01171 egg_group->add_matrix4(m4d); 01172 } 01173 return; 01174 } 01175 01176 //////////////////////////////////////////////////////////////////// 01177 // Function: MayaToEggConverter::get_joint_transform 01178 // Access: Private 01179 // Description: Extracts the transform on the indicated Maya node, 01180 // as appropriate for a joint in an animated character, 01181 // and applies it to the indicated node. This is 01182 // different from get_transform() in that it does not 01183 // respect the _transform_type flag, and it does not 01184 // consider the relative transforms within the egg file. 01185 //////////////////////////////////////////////////////////////////// 01186 void MayaToEggConverter:: 01187 get_joint_transform(const MDagPath &dag_path, EggGroup *egg_group) { 01188 // First, make sure there's not a transform on the group already. 01189 egg_group->clear_transform(); 01190 01191 MStatus status; 01192 MObject transformNode = dag_path.transform(&status); 01193 // This node has no transform - i.e., it's the world node 01194 if (!status && status.statusCode() == MStatus::kInvalidParameter) { 01195 return; 01196 } 01197 01198 MFnDagNode transform(transformNode, &status); 01199 if (!status) { 01200 status.perror("MFnDagNode constructor"); 01201 return; 01202 } 01203 01204 MTransformationMatrix matrix(transform.transformationMatrix()); 01205 01206 if (mayaegg_cat.is_spam()) { 01207 MVector t = matrix.translation(MSpace::kWorld); 01208 mayaegg_cat.spam() 01209 << " translation: [" 01210 << t[0] << ", " 01211 << t[1] << ", " 01212 << t[2] << "]\n"; 01213 double d[3]; 01214 MTransformationMatrix::RotationOrder rOrder; 01215 01216 matrix.getRotation(d, rOrder, MSpace::kWorld); 01217 mayaegg_cat.spam() 01218 << " rotation: [" 01219 << d[0] << ", " 01220 << d[1] << ", " 01221 << d[2] << "]\n"; 01222 matrix.getScale(d, MSpace::kWorld); 01223 mayaegg_cat.spam() 01224 << " scale: [" 01225 << d[0] << ", " 01226 << d[1] << ", " 01227 << d[2] << "]\n"; 01228 matrix.getShear(d, MSpace::kWorld); 01229 mayaegg_cat.spam() 01230 << " shear: [" 01231 << d[0] << ", " 01232 << d[1] << ", " 01233 << d[2] << "]\n"; 01234 } 01235 01236 MMatrix mat = matrix.asMatrix(); 01237 MMatrix ident_mat; 01238 ident_mat.setToIdentity(); 01239 01240 if (!mat.isEquivalent(ident_mat, 0.0001)) { 01241 egg_group->set_transform3d 01242 (LMatrix4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3], 01243 mat[1][0], mat[1][1], mat[1][2], mat[1][3], 01244 mat[2][0], mat[2][1], mat[2][2], mat[2][3], 01245 mat[3][0], mat[3][1], mat[3][2], mat[3][3])); 01246 } 01247 } 01248 01249 //////////////////////////////////////////////////////////////////// 01250 // Function: MayaToEggConverter::make_nurbs_surface 01251 // Access: Private 01252 // Description: Converts the indicated Maya NURBS surface to a 01253 // corresponding egg structure, and attaches it to the 01254 // indicated egg group. 01255 //////////////////////////////////////////////////////////////////// 01256 void MayaToEggConverter:: 01257 make_nurbs_surface(MayaNodeDesc *node_desc, const MDagPath &dag_path, 01258 MFnNurbsSurface &surface, EggGroup *egg_group) { 01259 MStatus status; 01260 string name = surface.name().asChar(); 01261 01262 if (mayaegg_cat.is_spam()) { 01263 mayaegg_cat.spam() 01264 << " numCVs: " 01265 << surface.numCVsInU() 01266 << " * " 01267 << surface.numCVsInV() 01268 << "\n"; 01269 mayaegg_cat.spam() 01270 << " numKnots: " 01271 << surface.numKnotsInU() 01272 << " * " 01273 << surface.numKnotsInV() 01274 << "\n"; 01275 mayaegg_cat.spam() 01276 << " numSpans: " 01277 << surface.numSpansInU() 01278 << " * " 01279 << surface.numSpansInV() 01280 << "\n"; 01281 } 01282 MayaShader *shader = _shaders.find_shader_for_node(surface.object(), _texture_copy, _texture_out_dir, _legacy_shader); 01283 01284 if (_polygon_output) { 01285 // If we want polygon output only, tesselate the NURBS and output 01286 // that. 01287 MTesselationParams params; 01288 params.setFormatType(MTesselationParams::kStandardFitFormat); 01289 params.setOutputType(MTesselationParams::kQuads); 01290 params.setStdFractionalTolerance(_polygon_tolerance); 01291 01292 // We'll create the tesselation as a sibling of the NURBS surface. 01293 // That way we inherit all of the transformations. 01294 MDagPath polyset_path = dag_path; 01295 MObject polyset_parent = polyset_path.node(); 01296 MObject polyset = 01297 surface.tesselate(params, polyset_parent, &status); 01298 if (!status) { 01299 status.perror("MFnNurbsSurface::tesselate"); 01300 return; 01301 } 01302 01303 status = polyset_path.push(polyset); 01304 if (!status) { 01305 status.perror("MDagPath::push"); 01306 } 01307 01308 MFnMesh polyset_fn(polyset, &status); 01309 if (!status) { 01310 status.perror("MFnMesh constructor"); 01311 return; 01312 } 01313 make_polyset(node_desc, polyset_path, polyset_fn, egg_group, shader); 01314 01315 // Now remove the polyset we created. 01316 MFnDagNode parent_node(polyset_parent, &status); 01317 if (!status) { 01318 status.perror("MFnDagNode constructor"); 01319 return; 01320 } 01321 status = parent_node.removeChild(polyset); 01322 if (!status) { 01323 status.perror("MFnDagNode::removeChild"); 01324 } 01325 01326 return; 01327 } 01328 01329 MPointArray cv_array; 01330 status = surface.getCVs(cv_array, MSpace::kWorld); 01331 if (!status) { 01332 status.perror("MFnNurbsSurface::getCVs"); 01333 return; 01334 } 01335 01336 // Also get out all the alternate blend shapes for the surface by 01337 // applying each morph slider one at a time. 01338 pvector<MPointArray> morph_cvs; 01339 if (_animation_convert == AC_model) { 01340 int num_sliders = node_desc->get_num_blend_descs(); 01341 morph_cvs.reserve(num_sliders); 01342 for (int i = 0; i < num_sliders; i++) { 01343 MayaBlendDesc *blend_desc = node_desc->get_blend_desc(i); 01344 01345 // Temporarily push the slider up to 1.0 so we can see what the 01346 // surface looks like at that value. 01347 blend_desc->set_slider(1.0); 01348 MPointArray cv_array; 01349 status = surface.getCVs(cv_array, MSpace::kWorld); 01350 blend_desc->set_slider(0.0); 01351 01352 if (!status) { 01353 status.perror("MFnNurbsSurface::getCVs"); 01354 return; 01355 } 01356 morph_cvs.push_back(cv_array); 01357 } 01358 } 01359 01360 MDoubleArray u_knot_array, v_knot_array; 01361 status = surface.getKnotsInU(u_knot_array); 01362 if (!status) { 01363 status.perror("MFnNurbsSurface::getKnotsInU"); 01364 return; 01365 } 01366 status = surface.getKnotsInV(v_knot_array); 01367 if (!status) { 01368 status.perror("MFnNurbsSurface::getKnotsInV"); 01369 return; 01370 } 01371 01372 MFnNurbsSurface::Form u_form = surface.formInU(); 01373 MFnNurbsSurface::Form v_form = surface.formInV(); 01374 01375 int u_degree = surface.degreeU(); 01376 int v_degree = surface.degreeV(); 01377 01378 int u_cvs = surface.numCVsInU(); 01379 int v_cvs = surface.numCVsInV(); 01380 01381 // Maya repeats CVS at the end for a periodic surface, and doesn't 01382 // count them in the joint weight array, below. 01383 int maya_u_cvs = (u_form == MFnNurbsSurface::kPeriodic) ? u_cvs - u_degree : u_cvs; 01384 int maya_v_cvs = (v_form == MFnNurbsSurface::kPeriodic) ? v_cvs - v_degree : v_cvs; 01385 01386 int u_knots = surface.numKnotsInU(); 01387 int v_knots = surface.numKnotsInV(); 01388 01389 assert(u_knots == u_cvs + u_degree - 1); 01390 assert(v_knots == v_cvs + v_degree - 1); 01391 01392 string vpool_name = name + ".cvs"; 01393 EggVertexPool *vpool = new EggVertexPool(vpool_name); 01394 egg_group->add_child(vpool); 01395 01396 EggNurbsSurface *egg_nurbs = new EggNurbsSurface(name); 01397 egg_nurbs->setup(u_degree + 1, v_degree + 1, 01398 u_knots + 2, v_knots + 2); 01399 01400 int i; 01401 01402 egg_nurbs->set_u_knot(0, u_knot_array[0]); 01403 for (i = 0; i < u_knots; i++) { 01404 egg_nurbs->set_u_knot(i + 1, u_knot_array[i]); 01405 } 01406 egg_nurbs->set_u_knot(u_knots + 1, u_knot_array[u_knots - 1]); 01407 01408 egg_nurbs->set_v_knot(0, v_knot_array[0]); 01409 for (i = 0; i < v_knots; i++) { 01410 egg_nurbs->set_v_knot(i + 1, v_knot_array[i]); 01411 } 01412 egg_nurbs->set_v_knot(v_knots + 1, v_knot_array[v_knots - 1]); 01413 01414 LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv(); 01415 01416 for (i = 0; i < egg_nurbs->get_num_cvs(); i++) { 01417 int ui = egg_nurbs->get_u_index(i); 01418 int vi = egg_nurbs->get_v_index(i); 01419 int maya_vi = v_cvs * ui + vi; 01420 01421 double v[4]; 01422 status = cv_array[maya_vi].get(v); 01423 if (!status) { 01424 status.perror("MPoint::get"); 01425 } else { 01426 EggVertex *vert = vpool->add_vertex(new EggVertex, i); 01427 LPoint4d p4d(v[0], v[1], v[2], v[3]); 01428 p4d = p4d * vertex_frame_inv; 01429 vert->set_pos(p4d); 01430 01431 // Now generate the morph targets for the vertex. 01432 if (!morph_cvs.empty()) { 01433 // Morph deltas are given in 3-d space, not in 4-d homogenous 01434 // space. 01435 LPoint3d p3d(v[0] / v[3], v[1] / v[3], v[2] / v[3]); 01436 01437 for (unsigned int si = 0; si < morph_cvs.size(); si++) { 01438 MayaBlendDesc *blend_desc = node_desc->get_blend_desc(si); 01439 status = morph_cvs[si][maya_vi].get(v); 01440 if (!status) { 01441 status.perror("MPoint::get"); 01442 } else { 01443 LPoint3d m3d(v[0] / v[3], v[1] / v[3], v[2] / v[3]); 01444 LVector3d delta = m3d - p3d; 01445 if (!delta.almost_equal(LVector3d::zero())) { 01446 EggMorphVertex dxyz(blend_desc->get_name(), delta); 01447 vert->_dxyzs.insert(dxyz); 01448 } 01449 } 01450 } 01451 } 01452 01453 egg_nurbs->add_vertex(vert); 01454 } 01455 } 01456 01457 // Now consider the trim curves, if any. 01458 unsigned num_trims = surface.numRegions(); 01459 int trim_curve_index = 0; 01460 for (unsigned ti = 0; ti < num_trims; ti++) { 01461 unsigned num_loops = surface.numBoundaries(ti); 01462 01463 if (num_loops > 0) { 01464 egg_nurbs->_trims.push_back(EggNurbsSurface::Trim()); 01465 EggNurbsSurface::Trim &egg_trim = egg_nurbs->_trims.back(); 01466 01467 for (unsigned li = 0; li < num_loops; li++) { 01468 egg_trim.push_back(EggNurbsSurface::Loop()); 01469 EggNurbsSurface::Loop &egg_loop = egg_trim.back(); 01470 01471 MFnNurbsSurface::BoundaryType type = 01472 surface.boundaryType(ti, li, &status); 01473 bool keep_loop = false; 01474 01475 if (!status) { 01476 status.perror("MFnNurbsSurface::BoundaryType"); 01477 } else { 01478 keep_loop = (type == MFnNurbsSurface::kInner || 01479 type == MFnNurbsSurface::kOuter); 01480 } 01481 01482 if (keep_loop) { 01483 unsigned num_edges = surface.numEdges(ti, li); 01484 for (unsigned ei = 0; ei < num_edges; ei++) { 01485 MObjectArray edge = surface.edge(ti, li, ei, true, &status); 01486 if (!status) { 01487 status.perror("MFnNurbsSurface::edge"); 01488 } else { 01489 unsigned num_segs = edge.length(); 01490 for (unsigned si = 0; si < num_segs; si++) { 01491 MObject segment = edge[si]; 01492 if (segment.hasFn(MFn::kNurbsCurve)) { 01493 MFnNurbsCurve curve(segment, &status); 01494 if (!status) { 01495 mayaegg_cat.error() 01496 << "Trim curve appears to be a nurbs curve, but isn't.\n"; 01497 } else { 01498 // Finally, we have a valid curve! 01499 EggNurbsCurve *egg_curve = 01500 make_trim_curve(curve, name, egg_group, trim_curve_index); 01501 trim_curve_index++; 01502 if (egg_curve != (EggNurbsCurve *)NULL) { 01503 egg_loop.push_back(egg_curve); 01504 } 01505 } 01506 } else { 01507 mayaegg_cat.error() 01508 << "Trim curve segment is not a nurbs curve.\n"; 01509 } 01510 } 01511 } 01512 } 01513 } 01514 } 01515 } 01516 } 01517 01518 // We add the NURBS to the group down here, after all of the vpools 01519 // for the trim curves have been added. 01520 egg_group->add_child(egg_nurbs); 01521 01522 if (shader != (MayaShader *)NULL) { 01523 set_shader_attributes(*egg_nurbs, *shader); 01524 } 01525 01526 // Now try to find the skinning information for the surface. 01527 bool got_weights = false; 01528 01529 pvector<EggGroup *> joints; 01530 MFloatArray weights; 01531 if (_animation_convert == AC_model) { 01532 got_weights = 01533 get_vertex_weights(dag_path, surface, joints, weights); 01534 } 01535 01536 if (got_weights && !joints.empty()) { 01537 int num_joints = joints.size(); 01538 int num_weights = (int)weights.length(); 01539 int num_verts = num_weights / num_joints; 01540 // The number of weights should be an even multiple of verts * 01541 // joints. 01542 nassertv(num_weights == num_verts * num_joints); 01543 01544 for (i = 0; i < egg_nurbs->get_num_cvs(); i++) { 01545 int ui = egg_nurbs->get_u_index(i) % maya_u_cvs; 01546 int vi = egg_nurbs->get_v_index(i) % maya_v_cvs; 01547 01548 int maya_vi = maya_v_cvs * ui + vi; 01549 nassertv(maya_vi < num_verts); 01550 EggVertex *vert = vpool->get_vertex(i); 01551 01552 for (int ji = 0; ji < num_joints; ++ji) { 01553 float weight = weights[maya_vi * num_joints + ji]; 01554 if (weight != 0.0f) { 01555 EggGroup *joint = joints[ji]; 01556 if (joint != (EggGroup *)NULL) { 01557 joint->ref_vertex(vert, weight); 01558 } 01559 } 01560 } 01561 } 01562 } 01563 } 01564 01565 //////////////////////////////////////////////////////////////////// 01566 // Function: MayaToEggConverter::make_trim_curve 01567 // Access: Private 01568 // Description: Converts the indicated Maya NURBS trim curve to a 01569 // corresponding egg structure, and returns it, or NULL 01570 // if there is a problem. 01571 //////////////////////////////////////////////////////////////////// 01572 EggNurbsCurve *MayaToEggConverter:: 01573 make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name, 01574 EggGroupNode *egg_group, int trim_curve_index) { 01575 if (mayaegg_cat.is_spam()) { 01576 mayaegg_cat.spam() 01577 << "Trim curve:\n"; 01578 mayaegg_cat.spam() 01579 << " numCVs: " 01580 << curve.numCVs() 01581 << "\n"; 01582 mayaegg_cat.spam() 01583 << " numKnots: " 01584 << curve.numKnots() 01585 << "\n"; 01586 mayaegg_cat.spam() 01587 << " numSpans: " 01588 << curve.numSpans() 01589 << "\n"; 01590 } 01591 01592 MStatus status; 01593 01594 MPointArray cv_array; 01595 status = curve.getCVs(cv_array, MSpace::kWorld); 01596 if (!status) { 01597 status.perror("MFnNurbsCurve::getCVs"); 01598 return (EggNurbsCurve *)NULL; 01599 } 01600 MDoubleArray knot_array; 01601 status = curve.getKnots(knot_array); 01602 if (!status) { 01603 status.perror("MFnNurbsCurve::getKnots"); 01604 return (EggNurbsCurve *)NULL; 01605 } 01606 01607 /* 01608 MFnNurbsCurve::Form form = curve.form(); 01609 */ 01610 01611 int degree = curve.degree(); 01612 int cvs = curve.numCVs(); 01613 int knots = curve.numKnots(); 01614 01615 assert(knots == cvs + degree - 1); 01616 01617 string trim_name = "trim" + format_string(trim_curve_index); 01618 01619 string vpool_name = nurbs_name + "." + trim_name; 01620 EggVertexPool *vpool = new EggVertexPool(vpool_name); 01621 egg_group->add_child(vpool); 01622 01623 EggNurbsCurve *egg_curve = new EggNurbsCurve(trim_name); 01624 egg_curve->setup(degree + 1, knots + 2); 01625 01626 int i; 01627 01628 egg_curve->set_knot(0, knot_array[0]); 01629 for (i = 0; i < knots; i++) { 01630 egg_curve->set_knot(i + 1, knot_array[i]); 01631 } 01632 egg_curve->set_knot(knots + 1, knot_array[knots - 1]); 01633 01634 for (i = 0; i < egg_curve->get_num_cvs(); i++) { 01635 double v[4]; 01636 MStatus status = cv_array[i].get(v); 01637 if (!status) { 01638 status.perror("MPoint::get"); 01639 } else { 01640 EggVertex vert; 01641 vert.set_pos(LPoint3d(v[0], v[1], v[3])); 01642 egg_curve->add_vertex(vpool->create_unique_vertex(vert)); 01643 } 01644 } 01645 01646 return egg_curve; 01647 } 01648 01649 //////////////////////////////////////////////////////////////////// 01650 // Function: MayaToEggConverter::make_nurbs_curve 01651 // Access: Private 01652 // Description: Converts the indicated Maya NURBS curve (a standalone 01653 // curve, not a trim curve) to a corresponding egg 01654 // structure and attaches it to the indicated egg group. 01655 //////////////////////////////////////////////////////////////////// 01656 void MayaToEggConverter:: 01657 make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve, 01658 EggGroup *egg_group) { 01659 MStatus status; 01660 string name = curve.name().asChar(); 01661 01662 if (mayaegg_cat.is_spam()) { 01663 mayaegg_cat.spam() 01664 << " numCVs: " 01665 << curve.numCVs() 01666 << "\n"; 01667 mayaegg_cat.spam() 01668 << " numKnots: " 01669 << curve.numKnots() 01670 << "\n"; 01671 mayaegg_cat.spam() 01672 << " numSpans: " 01673 << curve.numSpans() 01674 << "\n"; 01675 } 01676 01677 MPointArray cv_array; 01678 status = curve.getCVs(cv_array, MSpace::kWorld); 01679 if (!status) { 01680 status.perror("MFnNurbsCurve::getCVs"); 01681 return; 01682 } 01683 MDoubleArray knot_array; 01684 status = curve.getKnots(knot_array); 01685 if (!status) { 01686 status.perror("MFnNurbsCurve::getKnots"); 01687 return; 01688 } 01689 01690 /* 01691 MFnNurbsCurve::Form form = curve.form(); 01692 */ 01693 01694 int degree = curve.degree(); 01695 int cvs = curve.numCVs(); 01696 int knots = curve.numKnots(); 01697 01698 assert(knots == cvs + degree - 1); 01699 01700 string vpool_name = name + ".cvs"; 01701 EggVertexPool *vpool = new EggVertexPool(vpool_name); 01702 egg_group->add_child(vpool); 01703 01704 EggNurbsCurve *egg_curve = new EggNurbsCurve(name); 01705 egg_group->add_child(egg_curve); 01706 egg_curve->setup(degree + 1, knots + 2); 01707 01708 int i; 01709 01710 egg_curve->set_knot(0, knot_array[0]); 01711 for (i = 0; i < knots; i++) { 01712 egg_curve->set_knot(i + 1, knot_array[i]); 01713 } 01714 egg_curve->set_knot(knots + 1, knot_array[knots - 1]); 01715 01716 LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv(); 01717 01718 for (i = 0; i < egg_curve->get_num_cvs(); i++) { 01719 double v[4]; 01720 MStatus status = cv_array[i].get(v); 01721 if (!status) { 01722 status.perror("MPoint::get"); 01723 } else { 01724 EggVertex vert; 01725 LPoint4d p4d(v[0], v[1], v[2], v[3]); 01726 p4d = p4d * vertex_frame_inv; 01727 vert.set_pos(p4d); 01728 egg_curve->add_vertex(vpool->create_unique_vertex(vert)); 01729 } 01730 } 01731 MayaShader *shader = _shaders.find_shader_for_node(curve.object(), _texture_copy, _texture_out_dir, _legacy_shader); 01732 if (shader != (MayaShader *)NULL) { 01733 set_shader_attributes(*egg_curve, *shader); 01734 } 01735 } 01736 01737 //////////////////////////////////////////////////////////////////// 01738 // Function: MayaShader::round uvs 01739 // Access: Private 01740 // Description: given uvsets, round them up or down 01741 //////////////////////////////////////////////////////////////////// 01742 int MayaToEggConverter:: 01743 round(double value) { 01744 if (value < 0) 01745 return -(floor(-value + 0.5)); 01746 // or as an alternate use: 01747 // return ceil ( value - 0.5); 01748 else 01749 return floor( value + 0.5); 01750 } 01751 01752 //////////////////////////////////////////////////////////////////// 01753 // Function: MayaToEggConverter::make_polyset 01754 // Access: Private 01755 // Description: Converts the indicated Maya polyset to a bunch of 01756 // EggPolygons and parents them to the indicated egg 01757 // group. 01758 //////////////////////////////////////////////////////////////////// 01759 void MayaToEggConverter:: 01760 make_polyset(MayaNodeDesc *node_desc, const MDagPath &dag_path, 01761 const MFnMesh &mesh, EggGroup *egg_group, 01762 MayaShader *default_shader) { 01763 MStatus status; 01764 string name = mesh.name().asChar(); 01765 01766 MObject mesh_object = mesh.object(); 01767 bool maya_double_sided = false; 01768 get_bool_attribute(mesh_object, "doubleSided", maya_double_sided); 01769 01770 if (mayaegg_cat.is_spam()) { 01771 mayaegg_cat.spam() 01772 << " numPolygons: " 01773 << mesh.numPolygons() 01774 << "\n"; 01775 mayaegg_cat.spam() 01776 << " numVertices: " 01777 << mesh.numVertices() 01778 << "\n"; 01779 } 01780 01781 if (mesh.numPolygons() == 0) { 01782 if (mayaegg_cat.is_debug()) { 01783 mayaegg_cat.debug() 01784 << "Ignoring empty mesh " << name << "\n"; 01785 } 01786 return; 01787 } 01788 01789 string vpool_name = name + ".verts"; 01790 EggVertexPool *vpool = new EggVertexPool(vpool_name); 01791 egg_group->add_child(vpool); 01792 01793 // One way to convert the mesh would be to first get out all the 01794 // vertices in the mesh and add them into the vpool, then when we 01795 // traverse the polygons we would only have to index them into the 01796 // vpool according to their Maya vertex index. 01797 01798 // Unfortunately, since Maya may store multiple normals and/or 01799 // colors for each vertex according to which polygon it is in, that 01800 // approach won't necessarily work. In egg, those split-property 01801 // vertices have to become separate vertices. So instead of adding 01802 // all the vertices up front, we'll start with an empty vpool, and 01803 // add vertices to it on the fly. 01804 01805 MObject component_obj; 01806 MItMeshPolygon pi(dag_path, component_obj, &status); 01807 if (!status) { 01808 status.perror("MItMeshPolygon constructor"); 01809 return; 01810 } 01811 01812 MObjectArray shaders; 01813 MIntArray poly_shader_indices; 01814 01815 status = mesh.getConnectedShaders(dag_path.instanceNumber(), 01816 shaders, poly_shader_indices); 01817 if (!status) { 01818 status.perror("MFnMesh::getConnectedShaders"); 01819 } 01820 01821 // We will need to transform all vertices from world coordinate 01822 // space into the vertex space appropriate to this node. Usually, 01823 // this is the same thing as world coordinate space, and this matrix 01824 // will be identity; but if the node is under an instance 01825 // (particularly, for instance, a billboard) then the vertex space 01826 // will be different from world space. 01827 LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv(); 01828 01829 // Save these modeling flags for the check below. 01830 bool egg_vertex_color = false; 01831 bool egg_double_sided = false; 01832 if (egg_group->has_user_data(MayaEggGroupUserData::get_class_type())) { 01833 MayaEggGroupUserData *user_data = 01834 DCAST(MayaEggGroupUserData, egg_group->get_user_data()); 01835 egg_vertex_color = user_data->_vertex_color; 01836 egg_double_sided = user_data->_double_sided; 01837 } 01838 01839 bool double_sided = maya_double_sided; 01840 if (!_respect_maya_double_sided) { 01841 // If this flag is false, we respect the maya double-sided 01842 // settings only if the egg "double-sided" flag is also set. 01843 if (!egg_double_sided) { 01844 double_sided = false; 01845 } 01846 } 01847 01848 bool keep_all_uvsets = _keep_all_uvsets || node_desc->has_object_type("keep-all-uvsets"); 01849 if (node_desc->has_object_type("keep-all-uvsets")) { 01850 mayaegg_cat.info() << "will keep_all_uvsets" << endl; 01851 } 01852 01853 _shaders.bind_uvsets(mesh.object()); 01854 01855 while (!pi.isDone()) { 01856 EggPolygon *egg_poly = new EggPolygon; 01857 egg_group->add_child(egg_poly); 01858 01859 egg_poly->set_bface_flag(double_sided); 01860 01861 // Determine the MayaShader for this particular polygon. 01862 // There appears to be two diverging paths for any Maya node with a Material (MayaShader) on it 01863 // This next bit kicks us out into mayaShader et al. to pull textures and everything else. 01864 MayaShader *shader = NULL; 01865 int index = pi.index(); 01866 nassertv(index >= 0 && index < (int)poly_shader_indices.length()); 01867 int shader_index = poly_shader_indices[index]; 01868 01869 if (shader_index != -1) { 01870 nassertv(shader_index >= 0 && shader_index < (int)shaders.length()); 01871 MObject engine = shaders[shader_index]; 01872 shader = 01873 _shaders.find_shader_for_shading_engine(engine, _texture_copy, _texture_out_dir, _legacy_shader); //head out to the other classes 01874 //does this mean if we didn't find a Maya shader give it a default value anyway? 01875 } else if (default_shader != (MayaShader *)NULL) { 01876 shader = default_shader; 01877 } 01878 01879 const MayaShaderColorDef *default_color_def = NULL; 01880 01881 // And apply the shader properties to the polygon. 01882 if (shader != (MayaShader *)NULL) { 01883 set_shader_attributes(*egg_poly, *shader, true); 01884 default_color_def = shader->get_color_def(); 01885 } 01886 01887 // Should we extract the color from the vertices? Normally, in 01888 // Maya a texture completely replaces the vertex color, so we 01889 // should ignore the vertex color if we have a texture. 01890 01891 // However, this is an inconvenient property of Maya; sometimes we 01892 // really do want both vertex color and texture applied to the 01893 // same object. To allow this, we define the special egg flag 01894 // "vertex-color", which when set indicates that we should 01895 // respect the vertex color anyway. 01896 01897 // Furthermore, if _always_show_vertex_color is true, we pretend 01898 // that the "vertex-color" flag is always set. 01899 bool ignore_vertex_color = false; 01900 if ( default_color_def != (MayaShaderColorDef *)NULL) { 01901 ignore_vertex_color = default_color_def->_has_texture && !(egg_vertex_color || _always_show_vertex_color); 01902 } 01903 01904 Colorf poly_color(1.0f, 1.0f, 1.0f, 1.0f); 01905 if (!ignore_vertex_color) { 01906 // If we're respecting the vertex color, then remove the color 01907 // specification from the polygon (so we can apply it to the 01908 // vertices). 01909 poly_color = egg_poly->get_color(); 01910 egg_poly->clear_color(); 01911 } 01912 01913 // Get the vertices for the polygon. 01914 long num_verts = pi.polygonVertexCount(); 01915 long i; 01916 LPoint3d centroid(0.0, 0.0, 0.0); 01917 01918 if (default_color_def != (MayaShaderColorDef *)NULL && default_color_def->has_projection()) { 01919 // If the shader has a projection, we may need to compute the 01920 // polygon's centroid to avoid seams at the edges. 01921 for (i = 0; i < num_verts; i++) { 01922 MPoint p = pi.point(i, MSpace::kWorld); 01923 LPoint3d p3d(p[0], p[1], p[2]); 01924 p3d = p3d * vertex_frame_inv; 01925 centroid += p3d; 01926 } 01927 centroid /= (double)num_verts; 01928 } 01929 for (i = 0; i < num_verts; i++) { 01930 EggVertex vert; 01931 01932 MPoint p = pi.point(i, MSpace::kWorld); 01933 LPoint3d p3d(p[0] / p[3], p[1] / p[3], p[2] / p[3]); 01934 p3d = p3d * vertex_frame_inv; 01935 vert.set_pos(p3d); 01936 01937 MVector n; 01938 status = pi.getNormal(i, n, MSpace::kWorld); 01939 if (!status) { 01940 status.perror("MItMeshPolygon::getNormal"); 01941 } else { 01942 Normald n3d(n[0], n[1], n[2]); 01943 n3d = n3d * vertex_frame_inv; 01944 vert.set_normal(n3d); 01945 } 01946 01947 // Go thru all the texture references for this primitive and set uvs 01948 if (mayaegg_cat.is_debug()) { 01949 if (shader != (MayaShader *)NULL) { 01950 mayaegg_cat.debug() << "shader->_color.size is " << shader->_color.size() << endl; 01951 } 01952 mayaegg_cat.debug() << "primitive->tref.size is " << egg_poly->get_num_textures() << endl; 01953 } 01954 for (size_t ti=0; ti< _shaders._uvset_names.size(); ++ti) { 01955 // get the eggTexture pointer 01956 string uvset_name(_shaders._uvset_names[ti]); 01957 string panda_uvset_name = uvset_name; 01958 if (panda_uvset_name == "map1") { 01959 panda_uvset_name = "default"; 01960 } 01961 if (mayaegg_cat.is_debug()) { 01962 mayaegg_cat.debug() << "--uvset_name :" << uvset_name << endl; 01963 } 01964 01965 // get the shader color def that matches this EggTexture 01966 // Asad: optimizing uvset: to discard unused uvsets. This for 01967 // loop figures out which ones are unused. 01968 01969 bool keep_uv = keep_all_uvsets; 01970 bool project_uv = false; 01971 TexCoordd uv_projection; 01972 01973 if (shader != (MayaShader *)NULL) { 01974 for (size_t tj = 0; tj < shader->_all_maps.size(); ++tj) { 01975 MayaShaderColorDef *def = shader->_all_maps[tj]; 01976 if (def->_uvset_name == uvset_name) { 01977 if (mayaegg_cat.is_debug()) { 01978 mayaegg_cat.debug() << "matched colordef idx: " << tj << endl; 01979 } 01980 keep_uv = true; 01981 if (def->has_projection()) { 01982 project_uv = true; 01983 uv_projection = def->project_uv(p3d, centroid); 01984 } 01985 break; 01986 } 01987 } 01988 } 01989 01990 // if uvset is not used don't add it to the vertex 01991 if (!keep_uv) { 01992 if (mayaegg_cat.is_spam()) { 01993 mayaegg_cat.spam() << "discarding unused uvset " << uvset_name << endl; 01994 } 01995 continue; 01996 } 01997 01998 if (project_uv) { 01999 // If the shader has a projection, use it instead of the 02000 // polygon's built-in UV's. 02001 vert.set_uv(panda_uvset_name, uv_projection); 02002 } else { 02003 // Get the UV's from the polygon. 02004 float2 uvs; 02005 MString uv_mstring(uvset_name.c_str()); 02006 if (pi.hasUVs(uv_mstring, &status)) { 02007 status = pi.getUV(i, uvs, &uv_mstring); 02008 if (!status) { 02009 status.perror("MItMeshPolygon::getUV"); 02010 } else { 02011 if (_round_uvs) { 02012 if (uvs[0] > 1.0 || uvs[0] < -1.0) { 02013 // apply upto 1/1000th precision, but round up 02014 uvs[0] = (long)(uvs[0]*1000); 02015 mayaegg_cat.debug() << "before rounding uvs[0]: " << uvs[0] << endl; 02016 uvs[0] = (double)(round((double)uvs[0]/10.0)*10.0)/1000.0; 02017 mayaegg_cat.debug() << "after rounding uvs[0]: " << uvs[0] << endl; 02018 } 02019 if (uvs[1] > 1.0 || uvs[1] < -1.0) { 02020 uvs[1] = (long)(uvs[1]*1000); 02021 mayaegg_cat.debug() << "before rounding uvs[1]: " << uvs[1] << endl; 02022 uvs[1] = (double)(round((double)uvs[1]/10.0)*10.0)/1000.0; 02023 mayaegg_cat.debug() << "after rounding uvs[1]: " << uvs[1] << endl; 02024 } 02025 } 02026 vert.set_uv(panda_uvset_name, TexCoordd(uvs[0], uvs[1])); 02027 } 02028 } 02029 } 02030 } 02031 02032 if (!ignore_vertex_color) { 02033 if (mayaegg_cat.is_spam()) { 02034 mayaegg_cat.spam() << "poly_color = " << poly_color << endl; 02035 } 02036 set_vertex_color(vert,pi,i,shader,poly_color); 02037 } 02038 02039 vert.set_external_index(pi.vertexIndex(i, &status)); 02040 02041 egg_poly->add_vertex(vpool->create_unique_vertex(vert)); 02042 } 02043 02044 // Also get the face normal for the polygon. 02045 Normald face_normal; 02046 bool got_face_normal = false; 02047 02048 MVector n; 02049 status = pi.getNormal(n, MSpace::kWorld); 02050 if (!status) { 02051 status.perror("MItMeshPolygon::getNormal face"); 02052 } else { 02053 face_normal.set(n[0], n[1], n[2]); 02054 face_normal = face_normal * vertex_frame_inv; 02055 got_face_normal = true; 02056 egg_poly->set_normal(face_normal); 02057 } 02058 02059 // Now, check that the vertex ordering is consistent with the 02060 // direction of the normals. If not, reverse the vertex ordering 02061 // (since we have seen cases where Maya sets this in contradiction 02062 // to its normals). 02063 Normald order_normal; 02064 if (got_face_normal && egg_poly->calculate_normal(order_normal)) { 02065 if (order_normal.dot(face_normal) < 0.0) { 02066 egg_poly->reverse_vertex_ordering(); 02067 if (mayaegg_cat.is_debug()) { 02068 mayaegg_cat.debug() 02069 << "reversing polygon\n"; 02070 } 02071 } 02072 } 02073 02074 pi.next(); 02075 } 02076 if (mayaegg_cat.is_spam()) { 02077 mayaegg_cat.spam() << "done traversing polys" << endl; 02078 } 02079 02080 // Now that we've added all the polygons (and created all the 02081 // vertices), go back through the vertex pool and set up the 02082 // appropriate joint membership for each of the vertices. 02083 bool got_weights = false; 02084 02085 pvector<EggGroup *> joints; 02086 MFloatArray weights; 02087 if (_animation_convert == AC_model) { 02088 got_weights = 02089 get_vertex_weights(dag_path, mesh, joints, weights); 02090 } 02091 02092 if (got_weights && !joints.empty()) { 02093 int num_joints = joints.size(); 02094 int num_weights = (int)weights.length(); 02095 int num_verts = num_weights / num_joints; 02096 // The number of weights should be an even multiple of verts * 02097 // joints. 02098 nassertv(num_weights == num_verts * num_joints); 02099 02100 EggVertexPool::iterator vi; 02101 for (vi = vpool->begin(); vi != vpool->end(); ++vi) { 02102 EggVertex *vert = (*vi); 02103 int maya_vi = vert->get_external_index(); 02104 nassertv(maya_vi >= 0 && maya_vi < num_verts); 02105 02106 for (int ji = 0; ji < num_joints; ++ji) { 02107 float weight = weights[maya_vi * num_joints + ji]; 02108 if (weight != 0.0f) { 02109 EggGroup *joint = joints[ji]; 02110 if (joint != (EggGroup *)NULL) { 02111 joint->ref_vertex(vert, weight); 02112 } 02113 } 02114 } 02115 } 02116 } 02117 02118 02119 // We also need to compute the vertex morphs for the polyset, based 02120 // on whatever blend shapes may be present. This is similar to the 02121 // code in make_nurbs_surface(), except that since we don't have a 02122 // one-to-one relationship of egg vertices to Maya vertices, we have 02123 // to get the morphs down here, after we have added all of the egg 02124 // vertices. 02125 02126 if (_animation_convert == AC_model) { 02127 int num_orig_mesh_verts = mesh.numVertices(); 02128 02129 int num_sliders = node_desc->get_num_blend_descs(); 02130 for (int i = 0; i < num_sliders; i++) { 02131 MayaBlendDesc *blend_desc = node_desc->get_blend_desc(i); 02132 02133 // Temporarily push the slider up to 1.0 so we can see what the 02134 // surface looks like at that value. 02135 blend_desc->set_slider(1.0); 02136 02137 // We have to get the mesh object from the dag again after 02138 // fiddling with the slider. 02139 MFnMesh blend_mesh(dag_path, &status); 02140 if (!status) { 02141 mayaegg_cat.warning() 02142 << name << " no longer has a mesh after applying " 02143 << blend_desc->get_name() << "\n"; 02144 02145 } else { 02146 if (blend_mesh.numVertices() != num_orig_mesh_verts) { 02147 mayaegg_cat.warning() 02148 << "Ignoring " << blend_desc->get_name() << " for " 02149 << name << "; blend shape has " << blend_mesh.numVertices() 02150 << " vertices while original shape has " 02151 << num_orig_mesh_verts << ".\n"; 02152 02153 } else { 02154 MPointArray verts; 02155 status = blend_mesh.getPoints(verts, MSpace::kWorld); 02156 if (!status) { 02157 status.perror("MFnMesh::getPoints"); 02158 } else { 02159 int num_verts = (int)verts.length(); 02160 EggVertexPool::iterator vi; 02161 for (vi = vpool->begin(); vi != vpool->end(); ++vi) { 02162 EggVertex *vert = (*vi); 02163 int maya_vi = vert->get_external_index(); 02164 nassertv(maya_vi >= 0 && maya_vi < num_verts); 02165 02166 const MPoint &m = verts[maya_vi]; 02167 LPoint3d m3d(m[0] / m[3], m[1] / m[3], m[2] / m[3]); 02168 m3d = m3d * vertex_frame_inv; 02169 02170 LVector3d delta = m3d - vert->get_pos3(); 02171 if (!delta.almost_equal(LVector3d::zero())) { 02172 EggMorphVertex dxyz(blend_desc->get_name(), delta); 02173 vert->_dxyzs.insert(dxyz); 02174 } 02175 } 02176 } 02177 } 02178 } 02179 02180 blend_desc->set_slider(0.0); 02181 } 02182 } 02183 } 02184 02185 //////////////////////////////////////////////////////////////////// 02186 // Function: MayaToEggConverter::make_locator 02187 // Access: Private 02188 // Description: Locators are used in Maya to indicate a particular 02189 // position in space to the user or the modeler. We 02190 // represent that in egg with an ordinary Group node, 02191 // which we transform by the locator's position, so that 02192 // the indicated point becomes the origin at this node 02193 // and below. 02194 //////////////////////////////////////////////////////////////////// 02195 void MayaToEggConverter:: 02196 make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node, 02197 EggGroup *egg_group) { 02198 MStatus status; 02199 02200 unsigned int num_children = dag_node.childCount(); 02201 MObject locator; 02202 bool found_locator = false; 02203 for (unsigned int ci = 0; ci < num_children && !found_locator; ci++) { 02204 locator = dag_node.child(ci); 02205 found_locator = (locator.apiType() == MFn::kLocator); 02206 } 02207 02208 if (!found_locator) { 02209 mayaegg_cat.error() 02210 << "Couldn't find locator within locator node " 02211 << dag_path.fullPathName().asChar() << "\n"; 02212 return; 02213 } 02214 02215 LPoint3d p3d; 02216 if (!get_vec3d_attribute(locator, "localPosition", p3d)) { 02217 mayaegg_cat.error() 02218 << "Couldn't get position of locator " 02219 << dag_path.fullPathName().asChar() << "\n"; 02220 return; 02221 } 02222 02223 // We need to convert the position to world coordinates. For some 02224 // reason, Maya can only tell it to us in local coordinates. 02225 MMatrix mat = dag_path.inclusiveMatrix(&status); 02226 if (!status) { 02227 status.perror("Can't get coordinate space for locator"); 02228 return; 02229 } 02230 LMatrix4d n2w(mat[0][0], mat[0][1], mat[0][2], mat[0][3], 02231 mat[1][0], mat[1][1], mat[1][2], mat[1][3], 02232 mat[2][0], mat[2][1], mat[2][2], mat[2][3], 02233 mat[3][0], mat[3][1], mat[3][2], mat[3][3]); 02234 p3d = p3d * n2w; 02235 02236 // Now convert the locator point into the group's space. 02237 p3d = p3d * egg_group->get_node_frame_inv(); 02238 02239 egg_group->add_translate3d(p3d); 02240 } 02241 02242 02243 //////////////////////////////////////////////////////////////////// 02244 // Function: MayaToEggConverter::get_vertex_weights 02245 // Access: Private 02246 // Description: 02247 //////////////////////////////////////////////////////////////////// 02248 bool MayaToEggConverter:: 02249 get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh, 02250 pvector<EggGroup *> &joints, MFloatArray &weights) { 02251 MStatus status; 02252 02253 // Since we are working with a mesh the input attribute that 02254 // creates the mesh is named "inMesh" 02255 // 02256 MObject attr = mesh.attribute("inMesh"); 02257 02258 // Create the plug to the "inMesh" attribute then use the 02259 // DG iterator to walk through the DG, at the node level. 02260 // 02261 MPlug history(mesh.object(), attr); 02262 MItDependencyGraph it(history, MFn::kDependencyNode, 02263 MItDependencyGraph::kUpstream, 02264 MItDependencyGraph::kDepthFirst, 02265 MItDependencyGraph::kNodeLevel); 02266 02267 while (!it.isDone()) { 02268 // We will walk along the node level of the DG until we 02269 // spot a skinCluster node. 02270 // 02271 MObject c_node = it.thisNode(); 02272 if (c_node.hasFn(MFn::kSkinClusterFilter)) { 02273 // We've found the cluster handle. Try to get the weight 02274 // data. 02275 // 02276 MFnSkinCluster cluster(c_node, &status); 02277 if (!status) { 02278 status.perror("MFnSkinCluster constructor"); 02279 return false; 02280 } 02281 02282 // Get the set of objects that influence the vertices of this 02283 // mesh. Hopefully these will all be joints. 02284 MDagPathArray influence_objects; 02285 cluster.influenceObjects(influence_objects, &status); 02286 if (!status) { 02287 status.perror("MFnSkinCluster::influenceObjects"); 02288 02289 } else { 02290 // Fill up the vector with the corresponding table of egg 02291 // groups for each joint. 02292 joints.clear(); 02293 for (unsigned oi = 0; oi < influence_objects.length(); oi++) { 02294 MDagPath joint_dag_path = influence_objects[oi]; 02295 MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path); 02296 EggGroup *joint = _tree.get_egg_group(joint_node_desc); 02297 joints.push_back(joint); 02298 } 02299 02300 // Now use a component object to retrieve all of the weight 02301 // data in one API call. 02302 MFnSingleIndexedComponent sic; 02303 MObject sic_object = sic.create(MFn::kMeshVertComponent); 02304 sic.setCompleteData(mesh.numVertices()); 02305 unsigned influence_count; 02306 02307 status = cluster.getWeights(dag_path, sic_object, 02308 weights, influence_count); 02309 if (!status) { 02310 status.perror("MFnSkinCluster::getWeights"); 02311 } else { 02312 if (influence_count != influence_objects.length()) { 02313 mayaegg_cat.error() 02314 << "MFnSkinCluster::influenceObjects() returns " 02315 << influence_objects.length() 02316 << " objects, but MFnSkinCluster::getWeights() reports " 02317 << influence_count << " objects.\n"; 02318 02319 } else { 02320 // We've got the weights and the set of objects. That's all 02321 // we need. 02322 return true; 02323 } 02324 } 02325 } 02326 } else if (c_node.hasFn(MFn::kWeightGeometryFilt)) { 02327 // We've found the joint cluster handle. (rigid Binding) 02328 // 02329 MFnWeightGeometryFilter cluster(c_node, &status); 02330 if (!status) { 02331 status.perror("MFnWeightGeometryFilter constructor"); 02332 return false; 02333 } 02334 02335 MPlug matrix_plug = cluster.findPlug("matrix"); 02336 if (!matrix_plug.isNull()) { 02337 MPlugArray matrix_pa; 02338 matrix_plug.connectedTo(matrix_pa, true, false, &status); 02339 if (!status) { 02340 status.perror("Can't find connected Joint"); 02341 } else { 02342 MObject jointObj = matrix_pa[0].node(); 02343 MFnIkJoint jointFn = MFnIkJoint(jointObj, &status); 02344 if (!status) { 02345 status.perror("Can't find connected JointDag"); 02346 } else { 02347 joints.clear(); 02348 MDagPath joint_dag_path = MDagPath(); 02349 status = jointFn.getPath(joint_dag_path); 02350 if (!status) { 02351 status.perror("MFnIkJoint::dagPath"); 02352 } else { 02353 MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path); 02354 EggGroup *joint = _tree.get_egg_group(joint_node_desc); 02355 joints.push_back(joint); 02356 02357 // Now use a component object to retrieve all of the weight 02358 // data in one API call. 02359 MFnSingleIndexedComponent sic; 02360 MObject sic_object = sic.create(MFn::kMeshVertComponent); 02361 sic.setCompleteData(mesh.numVertices()); 02362 02363 status = cluster.getWeights(dag_path, sic_object, 02364 weights); 02365 if (!status) { 02366 status.perror("MFnWeightGeometryFilter::getWeights"); 02367 } else { 02368 // We've got the weights and the set of objects. That's all 02369 // we need. 02370 return true; 02371 } 02372 } 02373 } 02374 } 02375 } 02376 } 02377 02378 it.next(); 02379 } 02380 02381 // The mesh was not soft-skinned. 02382 return false; 02383 } 02384 02385 //////////////////////////////////////////////////////////////////// 02386 // Function: MayaToEggConverter::get_vertex_weights 02387 // Access: Private 02388 // Description: As above, for a NURBS surface instead of a polygon 02389 // mesh. 02390 //////////////////////////////////////////////////////////////////// 02391 bool MayaToEggConverter:: 02392 get_vertex_weights(const MDagPath &dag_path, const MFnNurbsSurface &surface, 02393 pvector<EggGroup *> &joints, MFloatArray &weights) { 02394 MStatus status; 02395 02396 // Since we are working with a NURBS surface the input attribute that 02397 // creates the surface is named "create" 02398 // 02399 MObject attr = surface.attribute("create"); 02400 02401 // Create the plug to the "create" attribute then use the 02402 // DG iterator to walk through the DG, at the node level. 02403 // 02404 MPlug history(surface.object(), attr); 02405 MItDependencyGraph it(history, MFn::kDependencyNode, 02406 MItDependencyGraph::kUpstream, 02407 MItDependencyGraph::kDepthFirst, 02408 MItDependencyGraph::kNodeLevel); 02409 02410 while (!it.isDone()) { 02411 // We will walk along the node level of the DG until we 02412 // spot a skinCluster node. 02413 // 02414 MObject c_node = it.thisNode(); 02415 if (c_node.hasFn(MFn::kSkinClusterFilter)) { 02416 // We've found the cluster handle. Try to get the weight 02417 // data. 02418 // 02419 MFnSkinCluster cluster(c_node, &status); 02420 if (!status) { 02421 status.perror("MFnSkinCluster constructor"); 02422 return false; 02423 } 02424 02425 // Get the set of objects that influence the vertices of this 02426 // surface. Hopefully these will all be joints. 02427 MDagPathArray influence_objects; 02428 cluster.influenceObjects(influence_objects, &status); 02429 if (!status) { 02430 status.perror("MFnSkinCluster::influenceObjects"); 02431 02432 } else { 02433 // Fill up the vector with the corresponding table of egg 02434 // groups for each joint. 02435 joints.clear(); 02436 for (unsigned oi = 0; oi < influence_objects.length(); oi++) { 02437 MDagPath joint_dag_path = influence_objects[oi]; 02438 MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path); 02439 EggGroup *joint = _tree.get_egg_group(joint_node_desc); 02440 joints.push_back(joint); 02441 } 02442 02443 // Now use a component object to retrieve all of the weight 02444 // data in one API call. 02445 MFnDoubleIndexedComponent dic; 02446 MObject dic_object = dic.create(MFn::kSurfaceCVComponent); 02447 dic.setCompleteData(surface.numCVsInU(), surface.numCVsInV()); 02448 unsigned influence_count; 02449 02450 status = cluster.getWeights(dag_path, dic_object, 02451 weights, influence_count); 02452 if (!status) { 02453 status.perror("MFnSkinCluster::getWeights"); 02454 } else { 02455 if (influence_count != influence_objects.length()) { 02456 mayaegg_cat.error() 02457 << "MFnSkinCluster::influenceObjects() returns " 02458 << influence_objects.length() 02459 << " objects, but MFnSkinCluster::getWeights() reports " 02460 << influence_count << " objects.\n"; 02461 02462 } else { 02463 // We've got the weights and the set of objects. That's all 02464 // we need. 02465 return true; 02466 } 02467 } 02468 } 02469 } 02470 02471 it.next(); 02472 } 02473 02474 // The surface was not soft-skinned. 02475 return false; 02476 } 02477 02478 //////////////////////////////////////////////////////////////////// 02479 // Function: MayaShader::set_shader_attributes 02480 // Access: Private 02481 // Description: Applies the known shader attributes to the indicated 02482 // egg primitive. Note: For multi-textures, Maya lists 02483 // the top most texture in slot 0. But Panda puts the 02484 // base texture at slot 0. Hence I parse the list of 02485 // textures from last to first. 02486 //////////////////////////////////////////////////////////////////// 02487 void MayaToEggConverter:: 02488 set_shader_attributes(EggPrimitive &primitive, const MayaShader &shader, 02489 bool mesh) { 02490 if (shader._legacy_mode) { 02491 set_shader_legacy(primitive, shader, mesh); 02492 } else { 02493 set_shader_modern(primitive, shader, mesh); 02494 } 02495 } 02496 02497 //////////////////////////////////////////////////////////////////// 02498 // Function: MayaToEggConverter::set_shader_modern 02499 // Access: Private 02500 // Description: The modern implementation of set_shader_attributes. 02501 // 02502 // In the modern codepath, the MayaShader is a direct, 02503 // literal representation of a list of EggTextures. 02504 // All this exporter has to do is translate the list 02505 // without interpretation. All the complex interpretation 02506 // is handled elsewhere, in the MayaShader module. 02507 //////////////////////////////////////////////////////////////////// 02508 void MayaToEggConverter:: 02509 set_shader_modern(EggPrimitive &primitive, const MayaShader &shader, 02510 bool mesh) { 02511 02512 for (size_t idx=0; idx < shader._all_maps.size(); idx++) { 02513 MayaShaderColorDef *def = shader._all_maps[idx]; 02514 if ((def->_is_alpha)&&(def->_opposite != 0)) { 02515 // This texture represents an alpha-filename. It doesn't get its own <Texture> 02516 continue; 02517 } 02518 02519 EggTexture tex(shader.get_name(), ""); 02520 tex.set_format(def->_is_alpha ? EggTexture::F_alpha : EggTexture::F_rgb); 02521 apply_texture_filename(tex, *def); 02522 if (def->_opposite) { 02523 apply_texture_alpha_filename(tex, *def); 02524 } 02525 apply_texture_uvprops(tex, *def); 02526 apply_texture_blendtype(tex, *def); 02527 tex.set_uv_name(def->get_panda_uvset_name()); 02528 02529 EggTexture *new_tex = 02530 _textures.create_unique_texture(tex, ~0); 02531 primitive.add_texture(new_tex); 02532 } 02533 } 02534 02535 //////////////////////////////////////////////////////////////////// 02536 // Function: MayaToEggConverter::set_shader_legacy 02537 // Access: Private 02538 // Description: The legacy implementation of set_shader_attributes. 02539 // The old behavior of the exporter is just plain weird. 02540 // It seems to be a result of an inexperienced coder 02541 // who made some core mistakes, and then patched them 02542 // up with kludges. It seems to produce plausible 02543 // results in certain specific cases, but overall, it 02544 // doesn't make any sense. Unfortunately, this weird 02545 // behavior cannot be discarded - vast numbers of 3D 02546 // models have been created that rely on this behavior. 02547 // The solution is to compartmentalize the weirdness. 02548 // The legacy codepath, when activated, implements the 02549 // old weird behavior. A brand-new codepath that 02550 // shares almost nothing with the legacy codepath 02551 // implements a much more straightforward behavior. 02552 //////////////////////////////////////////////////////////////////// 02553 void MayaToEggConverter:: 02554 set_shader_legacy(EggPrimitive &primitive, const MayaShader &shader, 02555 bool mesh) { 02556 02557 // determine if the base texture or any of the top texture need to be rgb only 02558 MayaShaderColorDef *color_def = NULL; 02559 bool is_rgb = false; 02560 bool is_decal = false; 02561 bool is_interpolate = false; 02562 int i; 02563 // last shader is the base so lets skip it 02564 for (i=0; i<(int)shader._color.size()-1; ++i) { 02565 color_def = shader.get_color_def(i); 02566 if (color_def->_has_texture) { 02567 if ((EggTexture::EnvType)color_def->_interpolate) { 02568 is_interpolate = true; 02569 } 02570 else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_modulate) { 02571 // Maya's multiply is slightly different than panda's. Unless, _keep_alpha is set, 02572 // we are dropping the alpha. 02573 if (!color_def->_keep_alpha) 02574 is_rgb = true; // modulate forces the alpha to be ignored 02575 } 02576 else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_decal) { 02577 is_decal = true; 02578 } 02579 } 02580 } 02581 02582 // we don't want an extra light stage for interpolate mode, it takes care of automatically 02583 if (is_interpolate) 02584 is_decal = false; 02585 02586 // new decal mode needs an extra dummy layers of textureStage 02587 EggTexture *dummy_tex = (EggTexture *)NULL; 02588 string dummy_uvset_name; 02589 02590 // In Maya, a polygon is either textured or colored. The texture, 02591 // if present, replaces the color. Also now there could be multiple textures 02592 const MayaShaderColorDef &trans_def = shader._transparency; 02593 for (i=shader._color.size()-1; i>=0; --i) { 02594 color_def = shader.get_color_def(i); 02595 if (mayaegg_cat.is_spam()) { 02596 mayaegg_cat.spam() << "slot " << i << ":got color_def: " << color_def << endl; 02597 } 02598 if (color_def->_has_texture || trans_def._has_texture) { 02599 EggTexture tex(shader.get_name(), ""); 02600 if (mayaegg_cat.is_debug()) { 02601 mayaegg_cat.debug() << "got shader name:" << shader.get_name() << endl; 02602 mayaegg_cat.debug() << "ssa:texture name[" << i << "]: " << color_def->_texture_name << endl; 02603 } 02604 02605 string uvset_name = _shaders.find_uv_link(color_def->_texture_name); 02606 if (mayaegg_cat.is_debug()) { 02607 mayaegg_cat.debug() << "ssa:corresponding uvset name is " << uvset_name << endl; 02608 } 02609 02610 if (color_def->_has_texture) { 02611 // If we have a texture on color, apply it as the filename. 02612 //if (mayaegg_cat.is_debug()) { 02613 //mayaegg_cat.debug() << "ssa:got texture name" << color_def->_texture_filename << endl; 02614 //} 02615 Filename filename = Filename::from_os_specific(color_def->_texture_filename); 02616 Filename fullpath, outpath; 02617 _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath); 02618 tex.set_filename(outpath); 02619 tex.set_fullpath(fullpath); 02620 apply_texture_uvprops(tex, *color_def); 02621 02622 // If we also have a texture on transparency, apply it as the 02623 // alpha filename. 02624 if (trans_def._has_texture) { 02625 if (color_def->_wrap_u != trans_def._wrap_u || 02626 color_def->_wrap_u != trans_def._wrap_u) { 02627 mayaegg_cat.warning() 02628 << "Shader " << shader.get_name() 02629 << " has contradictory wrap modes on color and texture.\n"; 02630 } 02631 02632 if (!compare_texture_uvprops(tex, trans_def)) { 02633 // Only report each broken shader once. 02634 static pset<string> bad_shaders; 02635 if (bad_shaders.insert(shader.get_name()).second) { 02636 mayaegg_cat.error() 02637 << "Color and transparency texture properties differ on shader " 02638 << shader.get_name() << "\n"; 02639 } 02640 } 02641 //tex.set_format(EggTexture::F_rgba); 02642 02643 // We should try to be smarter about whether the transparency 02644 // value is connected to the texture's alpha channel or to its 02645 // grayscale channel. However, I'm not sure how to detect 02646 // this at the moment; rather than spending days trying to 02647 // figure out, for now I'll just assume that if the same 02648 // texture image is used for both color and transparency, then 02649 // the artist meant to use the alpha channel for transparency. 02650 if (trans_def._texture_filename == color_def->_texture_filename) { 02651 // That means that we don't need to do anything special: use 02652 // all the channels of the texture. 02653 02654 } else { 02655 // Otherwise, pull the alpha channel from the other image 02656 // file. Ideally, we should figure out which channel from 02657 // the other image supplies alpha (and specify this via 02658 // set_alpha_file_channel()), but for now we assume it comes 02659 // from the grayscale data. 02660 filename = Filename::from_os_specific(trans_def._texture_filename); 02661 _path_replace->full_convert_path(filename, get_model_path(), 02662 fullpath, outpath); 02663 tex.set_alpha_filename(outpath); 02664 tex.set_alpha_fullpath(fullpath); 02665 } 02666 } else { 02667 // If there is no transparency texture specified, we don't 02668 // have any transparency, so tell the egg format to ignore any 02669 // alpha channel that might be on the color texture. 02670 //tex.set_format(EggTexture::F_rgb); 02671 } 02672 02673 if (shader._color.size() > 1) { 02674 // if multi-textured, first texture in maya is on top, so 02675 // last shader on the list is the base one, which should always pick up the alpha 02676 // from the texture file. But the top textures may have to strip the alpha 02677 if (i!=shader._color.size()-1) { 02678 if (!i && is_interpolate) { 02679 // this is the grass path mode where alpha on this texture determines 02680 // whether to show layer1 or layer2. Since by now other layers are set 02681 // lets change those to get this effect 02682 tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_interpolate); 02683 tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_previous); 02684 tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color); 02685 tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_last_saved_result); 02686 tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color); 02687 tex.set_combine_source(EggTexture::CC_rgb, 2, EggTexture::CS_texture); 02688 tex.set_combine_operand(EggTexture::CC_rgb, 2, EggTexture::CO_src_alpha); 02689 02690 tex.set_combine_mode(EggTexture::CC_alpha, EggTexture::CM_interpolate); 02691 tex.set_combine_source(EggTexture::CC_alpha, 0, EggTexture::CS_previous); 02692 tex.set_combine_operand(EggTexture::CC_alpha, 0, EggTexture::CO_src_alpha); 02693 tex.set_combine_source(EggTexture::CC_alpha, 1, EggTexture::CS_last_saved_result); 02694 tex.set_combine_operand(EggTexture::CC_alpha, 1, EggTexture::CO_src_alpha); 02695 tex.set_combine_source(EggTexture::CC_alpha, 2, EggTexture::CS_texture); 02696 tex.set_combine_operand(EggTexture::CC_alpha, 2, EggTexture::CO_src_alpha); 02697 } 02698 else { 02699 if (is_interpolate) { 02700 tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate); 02701 tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color); 02702 tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color); 02703 tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_texture); 02704 tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color); 02705 } 02706 else { 02707 tex.set_env_type((EggTexture::EnvType)color_def->_blend_type); 02708 if (tex.get_env_type() == EggTexture::ET_modulate) { 02709 if (color_def->_has_alpha_channel) { 02710 // lets caution the artist that they should not be using a alpha channel on 02711 // this texture. 02712 if (mayaegg_cat.is_spam()) { 02713 maya_cat.spam() 02714 << color_def->_texture_name 02715 << " should not have alpha channel in multiply mode: ignoring\n"; 02716 } 02717 } 02718 if (is_rgb) { 02719 //tex.set_alpha_mode(EggRenderMode::AM_off); // force alpha off 02720 tex.set_format(EggTexture::F_rgb); // Change the format to be rgb only 02721 } 02722 } 02723 } 02724 } 02725 } 02726 else { 02727 if (is_interpolate) { 02728 // base shader need to save result 02729 tex.set_saved_result(true); 02730 } 02731 else if (is_decal) { 02732 // decal in classic time, always overwrote the base color. That causes problem 02733 // when the polygon wants to be lit or wants to retain vertex/polygon color 02734 // In the new decal mode, we achieve this with a third dummy layer 02735 // copy this layer to a new dummy layer 02736 EggTexture texDummy(shader.get_name()+".dummy", ""); 02737 if (mayaegg_cat.is_debug()) { 02738 mayaegg_cat.debug() << "creating dummy shader: " << texDummy.get_name() << endl; 02739 } 02740 texDummy.set_filename(outpath); 02741 texDummy.set_fullpath(fullpath); 02742 apply_texture_uvprops(texDummy, *color_def); 02743 texDummy.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate); 02744 texDummy.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color); 02745 texDummy.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color); 02746 texDummy.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_previous); 02747 texDummy.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color); 02748 dummy_tex = _textures.create_unique_texture(texDummy, ~0); 02749 02750 // make this layer ET_replace 02751 tex.set_env_type(EggTexture::ET_replace); 02752 } 02753 } 02754 } 02755 } else { // trans_def._has_texture 02756 // We have a texture on transparency only. Apply it as the 02757 // primary filename, and set the format accordingly. 02758 Filename filename = Filename::from_os_specific(trans_def._texture_filename); 02759 Filename fullpath,outpath; 02760 _path_replace->full_convert_path(filename, get_model_path(), 02761 fullpath, outpath); 02762 tex.set_filename(outpath); 02763 tex.set_fullpath(fullpath); 02764 tex.set_format(EggTexture::F_alpha); 02765 apply_texture_uvprops(tex, trans_def); 02766 } 02767 02768 if (mayaegg_cat.is_debug()) { 02769 mayaegg_cat.debug() << "ssa:tref_name:" << tex.get_name() << endl; 02770 } 02771 if (is_rgb && i == (int)shader._color.size()-1) { 02772 // make base layer rgb only 02773 tex.set_format(EggTexture::F_rgb); // Change the format to be rgb only 02774 } 02775 EggTexture *new_tex = 02776 _textures.create_unique_texture(tex, ~0); 02777 02778 if (mesh) { 02779 if (uvset_name.find("not found") == -1) { 02780 primitive.add_texture(new_tex); 02781 color_def->_uvset_name.assign(uvset_name.c_str()); 02782 if (uvset_name != "map1") { 02783 new_tex->set_uv_name(uvset_name); 02784 } 02785 if (i == (int)shader._color.size()-1 && is_decal) { 02786 dummy_uvset_name.assign(color_def->_uvset_name); 02787 } 02788 } 02789 } else { 02790 primitive.add_texture(new_tex); 02791 if (color_def->_uvset_name != "map1") { 02792 new_tex->set_uv_name(color_def->_uvset_name); 02793 } 02794 } 02795 } 02796 } 02797 if (dummy_tex != (EggTexture *)NULL) { 02798 primitive.add_texture(dummy_tex); 02799 dummy_tex->set_uv_name(dummy_uvset_name); 02800 } 02801 // Also apply an overall color to the primitive. 02802 Colorf rgba = shader.get_rgba(); 02803 if (mayaegg_cat.is_spam()) { 02804 mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl; 02805 } 02806 02807 // The existence of a texture on either color channel completely 02808 // replaces the corresponding flat color. 02809 if (color_def && color_def->_has_texture) { 02810 rgba[0] = 1.0f; 02811 rgba[1] = 1.0f; 02812 rgba[2] = 1.0f; 02813 } 02814 if (trans_def._has_texture) { 02815 rgba[3] = 1.0f; 02816 } 02817 02818 // But the color gain always gets applied. 02819 if (color_def) { 02820 rgba[0] *= color_def->_color_gain[0]; 02821 rgba[1] *= color_def->_color_gain[1]; 02822 rgba[2] *= color_def->_color_gain[2]; 02823 rgba[3] *= color_def->_color_gain[3]; 02824 if (mayaegg_cat.is_spam()) { 02825 mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl; 02826 } 02827 } 02828 02829 primitive.set_color(rgba); 02830 02831 if (mayaegg_cat.is_spam()) { 02832 mayaegg_cat.spam() << " set_shader_attributes : end\n"; 02833 } 02834 } 02835 02836 //////////////////////////////////////////////////////////////////// 02837 // Function: MayaShader::apply_texture_uvprops 02838 // Access: Private 02839 // Description: Applies all the appropriate texture properties to the 02840 // EggTexture object, including wrap modes and texture 02841 // matrix. 02842 //////////////////////////////////////////////////////////////////// 02843 void MayaToEggConverter:: 02844 apply_texture_uvprops(EggTexture &tex, const MayaShaderColorDef &color_def) { 02845 // Let's mipmap all textures by default. 02846 tex.set_minfilter(EggTexture::FT_linear_mipmap_linear); 02847 tex.set_magfilter(EggTexture::FT_linear); 02848 02849 EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp; 02850 EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp; 02851 02852 tex.set_wrap_u(wrap_u); 02853 tex.set_wrap_v(wrap_v); 02854 02855 LMatrix3d mat = color_def.compute_texture_matrix(); 02856 if (!mat.almost_equal(LMatrix3d::ident_mat())) { 02857 tex.set_transform2d(mat); 02858 } 02859 } 02860 02861 //////////////////////////////////////////////////////////////////// 02862 // Function: MayaShader::apply_texture_blendtype 02863 // Access: Private 02864 // Description: Applies the blendtype to the EggTexture. 02865 //////////////////////////////////////////////////////////////////// 02866 void MayaToEggConverter:: 02867 apply_texture_blendtype(EggTexture &tex, const MayaShaderColorDef &color_def) { 02868 switch (color_def._blend_type) { 02869 case MayaShaderColorDef::BT_unspecified: 02870 tex.set_env_type(EggTexture::ET_unspecified); 02871 return; 02872 case MayaShaderColorDef::BT_modulate: 02873 tex.set_env_type(EggTexture::ET_modulate); 02874 return; 02875 case MayaShaderColorDef::BT_decal: 02876 tex.set_env_type(EggTexture::ET_decal); 02877 return; 02878 case MayaShaderColorDef::BT_blend: 02879 tex.set_env_type(EggTexture::ET_blend); 02880 return; 02881 case MayaShaderColorDef::BT_replace: 02882 tex.set_env_type(EggTexture::ET_replace); 02883 return; 02884 case MayaShaderColorDef::BT_add: 02885 tex.set_env_type(EggTexture::ET_add); 02886 return; 02887 case MayaShaderColorDef::BT_blend_color_scale: 02888 tex.set_env_type(EggTexture::ET_blend_color_scale); 02889 return; 02890 case MayaShaderColorDef::BT_modulate_glow: 02891 tex.set_env_type(EggTexture::ET_modulate_glow); 02892 return; 02893 case MayaShaderColorDef::BT_modulate_gloss: 02894 tex.set_env_type(EggTexture::ET_modulate_gloss); 02895 return; 02896 case MayaShaderColorDef::BT_normal: 02897 tex.set_env_type(EggTexture::ET_normal); 02898 return; 02899 case MayaShaderColorDef::BT_normal_height: 02900 tex.set_env_type(EggTexture::ET_normal_height); 02901 return; 02902 case MayaShaderColorDef::BT_glow: 02903 tex.set_env_type(EggTexture::ET_glow); 02904 return; 02905 case MayaShaderColorDef::BT_gloss: 02906 tex.set_env_type(EggTexture::ET_gloss); 02907 return; 02908 case MayaShaderColorDef::BT_height: 02909 tex.set_env_type(EggTexture::ET_height); 02910 return; 02911 case MayaShaderColorDef::BT_selector: 02912 tex.set_env_type(EggTexture::ET_selector); 02913 return; 02914 } 02915 } 02916 02917 //////////////////////////////////////////////////////////////////// 02918 // Function: MayaShader::apply_texture_filename 02919 // Access: Private 02920 // Description: Applies the filename to the EggTexture. 02921 //////////////////////////////////////////////////////////////////// 02922 void MayaToEggConverter:: 02923 apply_texture_filename(EggTexture &tex, const MayaShaderColorDef &def) { 02924 Filename filename = Filename::from_os_specific(def._texture_filename); 02925 Filename fullpath, outpath; 02926 _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath); 02927 tex.set_filename(outpath); 02928 tex.set_fullpath(fullpath); 02929 } 02930 02931 //////////////////////////////////////////////////////////////////// 02932 // Function: MayaShader::apply_texture_alpha_filename 02933 // Access: Private 02934 // Description: Applies the alpha filename to the EggTexture. 02935 //////////////////////////////////////////////////////////////////// 02936 void MayaToEggConverter:: 02937 apply_texture_alpha_filename(EggTexture &tex, const MayaShaderColorDef &def) { 02938 if (def._opposite) { 02939 tex.set_format(EggTexture::F_rgba); 02940 if (def._opposite->_texture_filename != def._texture_filename) { 02941 Filename filename = Filename::from_os_specific(def._opposite->_texture_filename); 02942 Filename fullpath, outpath; 02943 _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath); 02944 tex.set_alpha_filename(outpath); 02945 tex.set_alpha_fullpath(fullpath); 02946 } 02947 } 02948 } 02949 02950 //////////////////////////////////////////////////////////////////// 02951 // Function: MayaShader::compare_texture_uvprops 02952 // Access: Private 02953 // Description: Compares the texture properties already on the 02954 // texture (presumably set by a previous call to 02955 // apply_texture_uvprops()) and returns false if they 02956 // differ from that specified by the indicated color_def 02957 // object, or true if they match. 02958 //////////////////////////////////////////////////////////////////// 02959 bool MayaToEggConverter:: 02960 compare_texture_uvprops(EggTexture &tex, 02961 const MayaShaderColorDef &color_def) { 02962 bool okflag = true; 02963 02964 EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp; 02965 EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp; 02966 02967 if (wrap_u != tex.determine_wrap_u()) { 02968 // Choose the more general of the two. 02969 if (wrap_u == EggTexture::WM_repeat) { 02970 tex.set_wrap_u(wrap_u); 02971 } 02972 okflag = false; 02973 } 02974 if (wrap_v != tex.determine_wrap_v()) { 02975 if (wrap_v == EggTexture::WM_repeat) { 02976 tex.set_wrap_v(wrap_v); 02977 } 02978 okflag = false; 02979 } 02980 02981 LMatrix3d m = color_def.compute_texture_matrix(); 02982 LMatrix4d mat4(m(0, 0), m(0, 1), 0.0, m(0, 2), 02983 m(1, 0), m(1, 1), 0.0, m(1, 2), 02984 0.0, 0.0, 1.0, 0.0, 02985 m(2, 0), m(2, 1), 0.0, m(2, 2)); 02986 if (!mat4.almost_equal(tex.get_transform3d())) { 02987 okflag = false; 02988 } 02989 02990 return okflag; 02991 } 02992 02993 //////////////////////////////////////////////////////////////////// 02994 // Function: MayaShader::reparent_decals 02995 // Access: Private 02996 // Description: Recursively walks the egg hierarchy, reparenting 02997 // "decal" type nodes below their corresponding 02998 // "decalbase" type nodes, and setting the flags. 02999 // 03000 // Returns true on success, false if some nodes were 03001 // incorrect. 03002 //////////////////////////////////////////////////////////////////// 03003 bool MayaToEggConverter:: 03004 reparent_decals(EggGroupNode *egg_parent) { 03005 bool okflag = true; 03006 03007 // First, walk through all children of this node, looking for the 03008 // one decal base, if any. 03009 EggGroup *decal_base = (EggGroup *)NULL; 03010 pvector<EggGroup *> decal_children; 03011 03012 EggGroupNode::iterator ci; 03013 for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) { 03014 EggNode *child = (*ci); 03015 if (child->is_of_type(EggGroup::get_class_type())) { 03016 EggGroup *child_group = DCAST(EggGroup, child); 03017 if (child_group->has_object_type("decalbase")) { 03018 if (decal_base != (EggNode *)NULL) { 03019 mayaegg_cat.error() 03020 << "Two children of " << egg_parent->get_name() 03021 << " both have decalbase set: " << decal_base->get_name() 03022 << " and " << child_group->get_name() << "\n"; 03023 okflag = false; 03024 } 03025 child_group->remove_object_type("decalbase"); 03026 decal_base = child_group; 03027 03028 } else if (child_group->has_object_type("decal")) { 03029 child_group->remove_object_type("decal"); 03030 decal_children.push_back(child_group); 03031 } 03032 } 03033 } 03034 03035 if (decal_base == (EggGroup *)NULL) { 03036 if (!decal_children.empty()) { 03037 mayaegg_cat.warning() 03038 << decal_children.front()->get_name() 03039 << " has decal, but no sibling node has decalbase.\n"; 03040 } 03041 03042 } else { 03043 if (decal_children.empty()) { 03044 mayaegg_cat.warning() 03045 << decal_base->get_name() 03046 << " has decalbase, but no sibling nodes have decal.\n"; 03047 03048 } else { 03049 // All the decal children get moved to be a child of decal base. 03050 // This usually will not affect the vertex positions, but it 03051 // could if the decal base has a transform and the decal child 03052 // is an instance node. So don't do that. 03053 pvector<EggGroup *>::iterator di; 03054 for (di = decal_children.begin(); di != decal_children.end(); ++di) { 03055 EggGroup *child_group = (*di); 03056 decal_base->add_child(child_group); 03057 } 03058 03059 // Also set the decal state on the base. 03060 decal_base->set_decal_flag(true); 03061 } 03062 } 03063 03064 // Now recurse on each of the child nodes. 03065 for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) { 03066 EggNode *child = (*ci); 03067 if (child->is_of_type(EggGroupNode::get_class_type())) { 03068 EggGroupNode *child_group = DCAST(EggGroupNode, child); 03069 if (!reparent_decals(child_group)) { 03070 okflag = false; 03071 } 03072 } 03073 } 03074 03075 return okflag; 03076 } 03077 03078 //////////////////////////////////////////////////////////////////// 03079 // Function: MayaShader::string_transform_type 03080 // Access: Public, Static 03081 // Description: Returns the TransformType value corresponding to the 03082 // indicated string, or TT_invalid. 03083 //////////////////////////////////////////////////////////////////// 03084 MayaToEggConverter::TransformType MayaToEggConverter:: 03085 string_transform_type(const string &arg) { 03086 if (cmp_nocase(arg, "all") == 0) { 03087 return TT_all; 03088 } else if (cmp_nocase(arg, "model") == 0) { 03089 return TT_model; 03090 } else if (cmp_nocase(arg, "dcs") == 0) { 03091 return TT_dcs; 03092 } else if (cmp_nocase(arg, "none") == 0) { 03093 return TT_none; 03094 } else { 03095 return TT_invalid; 03096 } 03097 } 03098 //////////////////////////////////////////////////////////////////// 03099 // Function: MayaShader::MayaToEggConverter::set_vertex_color 03100 // Access: Private 03101 // Description: Checks to see if we're using legacy or modern 03102 // shaders and based on the result, it passes 03103 // the vertex color calculations off to either 03104 // the legacy or modern vertex color functions. 03105 //////////////////////////////////////////////////////////////////// 03106 void MayaToEggConverter:: 03107 set_vertex_color(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const Colorf &color) { 03108 if (shader == (MayaShader *)NULL || shader->_legacy_mode) { 03109 set_vertex_color_legacy(vert, pi, vert_index, shader, color); 03110 } else { 03111 set_vertex_color_modern(vert, pi, vert_index, shader, color); 03112 } 03113 } 03114 //////////////////////////////////////////////////////////////////// 03115 // Function: MayaShader::MayaToEggConverter::set_vertex_color_legacy 03116 // Access: Private 03117 // Description: Calls set_color on an EggVertex, determining the 03118 // correct color values, based on the shader, vert_color 03119 // Maya's vertex & flat color(s). This is the original 03120 // implementation that works only on Lambert shaders/materials 03121 // in Maya. 03122 //////////////////////////////////////////////////////////////////// 03123 void MayaToEggConverter:: 03124 set_vertex_color_legacy(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const Colorf &color){ 03125 if (pi.hasColor()) { 03126 MColor c; 03127 MStatus status = pi.getColor(c, vert_index); 03128 if (!status) { 03129 status.perror("MItMeshPolygon::getColor"); 03130 } else { 03131 // I saw instances where the color components exceeded 1.0 03132 // so lets clamp the values to 0 to 1 03133 c /= 1.0; 03134 // The vertex color is a color scale that modifies the 03135 // polygon color, not an override that replaces it. 03136 vert.set_color(Colorf(c.r * color[0], c.g * color[1], c.b * color[2], c.a * color[3])); 03137 03138 if (mayaegg_cat.is_spam()) { 03139 mayaegg_cat.spam() << "maya_color = " << c.r << " " << c.g << " " << c.b << " " << c.a << endl; 03140 mayaegg_cat.spam() << "vert_color = " << vert.get_color() << endl; 03141 } 03142 } 03143 } else { 03144 vert.set_color(color); 03145 } 03146 03147 } 03148 //////////////////////////////////////////////////////////////////// 03149 // Function: MayaShader::MayaToEggConverter::set_vertex_color_modern 03150 // Access: Private 03151 // Description: Calls set_color on an EggVertex, determining the 03152 // correct color values, based on the shader, vert_color 03153 // Maya's vertex & flat color(s). This implementation 03154 // is designed to work specifically with Phong materials 03155 // or shaders. 03156 //////////////////////////////////////////////////////////////////// 03157 void MayaToEggConverter:: 03158 set_vertex_color_modern(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const Colorf &color) { 03159 // If there's an explicit vertex color, output it. 03160 if (pi.hasColor(vert_index)) { 03161 MColor c; 03162 MStatus status = pi.getColor(c, vert_index); 03163 if (status) { 03164 vert.set_color(Colorf(c.r, c.g, c.b, c.a)); 03165 return; 03166 } 03167 } 03168 03169 // If there's no explicit color, use flat color, or white on a textured model. 03170 if (shader->_color_maps.empty()) { 03171 const Colord &c = shader->_flat_color; 03172 vert.set_color(Colorf((float)c[0], (float)c[1], (float)c[2], (float)c[3])); 03173 } else { 03174 //there's no explicit color anywhere, must be textured (or blank) 03175 vert.set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); 03176 } 03177 }