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