Panda3D
 All Classes Functions Variables Enumerations
mayaToEggConverter.cxx
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 &copy) :
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       vert.set_external_index2(pi.normalIndex(i, &status));
02036 
02037       egg_poly->add_vertex(vpool->create_unique_vertex(vert));
02038     }
02039 
02040     // Also get the face normal for the polygon.
02041     LNormald face_normal;
02042     bool got_face_normal = false;
02043 
02044     MVector n;
02045     status = pi.getNormal(n, MSpace::kWorld);
02046     if (!status) {
02047       status.perror("MItMeshPolygon::getNormal face");
02048     } else {
02049       face_normal.set(n[0], n[1], n[2]);
02050       face_normal = face_normal * vertex_frame_inv;
02051       got_face_normal = true;
02052       egg_poly->set_normal(face_normal);
02053     }
02054 
02055     // Now, check that the vertex ordering is consistent with the
02056     // direction of the normals.  If not, reverse the vertex ordering
02057     // (since we have seen cases where Maya sets this in contradiction
02058     // to its normals).
02059     LNormald order_normal;
02060     if (got_face_normal && egg_poly->calculate_normal(order_normal)) {
02061       if (order_normal.dot(face_normal) < 0.0) {
02062         egg_poly->reverse_vertex_ordering();
02063         if (mayaegg_cat.is_debug()) {
02064           mayaegg_cat.debug()
02065             << "reversing polygon\n";
02066         }
02067       }
02068     }
02069   
02070     pi.next();
02071   }
02072   if (mayaegg_cat.is_spam()) {
02073     mayaegg_cat.spam() << "done traversing polys" << endl;
02074   }
02075 
02076   // Now that we've added all the polygons (and created all the
02077   // vertices), go back through the vertex pool and set up the
02078   // appropriate joint membership for each of the vertices.
02079   bool got_weights = false;
02080 
02081   pvector<EggGroup *> joints;
02082   MFloatArray weights;
02083   if (_animation_convert == AC_model) {
02084     got_weights = 
02085       get_vertex_weights(dag_path, mesh, joints, weights);
02086   }
02087 
02088   if (got_weights && !joints.empty()) {
02089     int num_joints = joints.size();
02090     int num_weights = (int)weights.length();
02091     int num_verts = num_weights / num_joints;
02092     // The number of weights should be an even multiple of verts *
02093     // joints.
02094     nassertv(num_weights == num_verts * num_joints);
02095 
02096     EggVertexPool::iterator vi;
02097     for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
02098       EggVertex *vert = (*vi);
02099       int maya_vi = vert->get_external_index();
02100       nassertv(maya_vi >= 0 && maya_vi < num_verts);
02101 
02102       for (int ji = 0; ji < num_joints; ++ji) {
02103         PN_stdfloat weight = weights[maya_vi * num_joints + ji];
02104         if (weight != 0.0f) {
02105           EggGroup *joint = joints[ji];
02106           if (joint != (EggGroup *)NULL) {
02107             joint->ref_vertex(vert, weight);
02108           }
02109         }
02110       }
02111     }
02112   }
02113 
02114 
02115   // We also need to compute the vertex morphs for the polyset, based
02116   // on whatever blend shapes may be present.  This is similar to the
02117   // code in make_nurbs_surface(), except that since we don't have a
02118   // one-to-one relationship of egg vertices to Maya vertices, we have
02119   // to get the morphs down here, after we have added all of the egg
02120   // vertices.
02121 
02122   if (_animation_convert == AC_model) {
02123     int num_orig_mesh_verts = mesh.numVertices();
02124 
02125     int num_sliders = node_desc->get_num_blend_descs();
02126     for (int i = 0; i < num_sliders; i++) {
02127       MayaBlendDesc *blend_desc = node_desc->get_blend_desc(i);
02128 
02129       // Temporarily push the slider up to 1.0 so we can see what the
02130       // surface looks like at that value.
02131       blend_desc->set_slider(1.0);
02132 
02133       // We have to get the mesh object from the dag again after
02134       // fiddling with the slider.
02135       MFnMesh blend_mesh(dag_path, &status);
02136       if (!status) {
02137         mayaegg_cat.warning()
02138           << name << " no longer has a mesh after applying "
02139           << blend_desc->get_name() << "\n";
02140 
02141       } else {
02142         if (blend_mesh.numVertices() != num_orig_mesh_verts) {
02143           mayaegg_cat.warning()
02144             << "Ignoring " << blend_desc->get_name() << " for " 
02145             << name << "; blend shape has " << blend_mesh.numVertices()
02146             << " vertices while original shape has " 
02147             << num_orig_mesh_verts << ".\n";
02148           
02149         } else {
02150           MPointArray verts;
02151           status = blend_mesh.getPoints(verts, MSpace::kWorld);
02152           if (!status) {
02153             status.perror("MFnMesh::getPoints");
02154           } else {
02155             int num_verts = (int)verts.length();
02156             EggVertexPool::iterator vi;
02157             for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
02158               EggVertex *vert = (*vi);
02159               int maya_vi = vert->get_external_index();
02160               nassertv(maya_vi >= 0 && maya_vi < num_verts);
02161               
02162               const MPoint &m = verts[maya_vi];
02163               LPoint3d m3d(m[0] / m[3], m[1] / m[3], m[2] / m[3]);
02164               m3d = m3d * vertex_frame_inv;
02165               
02166               LVector3d delta = m3d - vert->get_pos3();
02167               if (!delta.almost_equal(LVector3d::zero())) {
02168                 EggMorphVertex dxyz(blend_desc->get_name(), delta);
02169                 vert->_dxyzs.insert(dxyz);
02170               }
02171             }
02172           }
02173 
02174           MFloatVectorArray norms;
02175           status = blend_mesh.getNormals(norms, MSpace::kWorld);
02176           if (!status) {
02177             status.perror("MFnMesh::getNormals");
02178           } else {
02179             int num_norms = (int)norms.length();
02180             EggVertexPool::iterator vi;
02181             for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
02182               EggVertex *vert = (*vi);
02183               int maya_vi = vert->get_external_index2();
02184               nassertv(maya_vi >= 0 && maya_vi < num_norms);
02185               
02186               const MFloatVector &m = norms[maya_vi];
02187               LVector3d m3d(m[0], m[1], m[2]);
02188               m3d = m3d * vertex_frame_inv;
02189               
02190               LNormald delta = m3d - vert->get_normal();
02191               if (!delta.almost_equal(LVector3d::zero())) {
02192                 EggMorphNormal dnormal(blend_desc->get_name(), delta);
02193                 vert->_dnormals.insert(dnormal);
02194               }
02195             }
02196           }
02197         }
02198       }
02199 
02200       blend_desc->set_slider(0.0);
02201     }
02202   }
02203 }
02204 
02205 ////////////////////////////////////////////////////////////////////
02206 //     Function: MayaToEggConverter::make_locator
02207 //       Access: Private
02208 //  Description: Locators are used in Maya to indicate a particular
02209 //               position in space to the user or the modeler.  We
02210 //               represent that in egg with an ordinary Group node,
02211 //               which we transform by the locator's position, so that
02212 //               the indicated point becomes the origin at this node
02213 //               and below.
02214 ////////////////////////////////////////////////////////////////////
02215 void MayaToEggConverter::
02216 make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
02217              EggGroup *egg_group) {
02218   MStatus status;
02219 
02220   unsigned int num_children = dag_node.childCount();
02221   MObject locator;
02222   bool found_locator = false;
02223   for (unsigned int ci = 0; ci < num_children && !found_locator; ci++) {
02224     locator = dag_node.child(ci);
02225     found_locator = (locator.apiType() == MFn::kLocator);
02226   }
02227 
02228   if (!found_locator) {
02229     mayaegg_cat.error()
02230       << "Couldn't find locator within locator node " 
02231       << dag_path.fullPathName().asChar() << "\n";
02232     return;
02233   }
02234 
02235   LPoint3d p3d;
02236   if (!get_vec3d_attribute(locator, "localPosition", p3d)) {
02237     mayaegg_cat.error()
02238       << "Couldn't get position of locator " 
02239       << dag_path.fullPathName().asChar() << "\n";
02240     return;
02241   }
02242 
02243   // We need to convert the position to world coordinates.  For some
02244   // reason, Maya can only tell it to us in local coordinates.
02245   MMatrix mat = dag_path.inclusiveMatrix(&status);
02246   if (!status) {
02247     status.perror("Can't get coordinate space for locator");
02248     return;
02249   }
02250   LMatrix4d n2w(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
02251                 mat[1][0], mat[1][1], mat[1][2], mat[1][3],
02252                 mat[2][0], mat[2][1], mat[2][2], mat[2][3],
02253                 mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
02254   p3d = p3d * n2w;
02255 
02256   // Now convert the locator point into the group's space.
02257   p3d = p3d * egg_group->get_node_frame_inv();
02258 
02259   egg_group->add_translate3d(p3d);
02260 }
02261 
02262 
02263 ////////////////////////////////////////////////////////////////////
02264 //     Function: MayaToEggConverter::get_vertex_weights
02265 //       Access: Private
02266 //  Description: 
02267 ////////////////////////////////////////////////////////////////////
02268 bool MayaToEggConverter::
02269 get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
02270                    pvector<EggGroup *> &joints, MFloatArray &weights) {
02271   MStatus status;
02272   
02273   // Since we are working with a mesh the input attribute that 
02274   // creates the mesh is named "inMesh" 
02275   // 
02276   MObject attr = mesh.attribute("inMesh"); 
02277   
02278   // Create the plug to the "inMesh" attribute then use the 
02279   // DG iterator to walk through the DG, at the node level.
02280   // 
02281   MPlug history(mesh.object(), attr); 
02282   MItDependencyGraph it(history, MFn::kDependencyNode, 
02283                         MItDependencyGraph::kUpstream, 
02284                         MItDependencyGraph::kDepthFirst, 
02285                         MItDependencyGraph::kNodeLevel);
02286 
02287   while (!it.isDone()) {
02288     // We will walk along the node level of the DG until we 
02289     // spot a skinCluster node.
02290     // 
02291     MObject c_node = it.thisNode(); 
02292     if (c_node.hasFn(MFn::kSkinClusterFilter)) { 
02293       // We've found the cluster handle. Try to get the weight
02294       // data.
02295       // 
02296       MFnSkinCluster cluster(c_node, &status); 
02297       if (!status) {
02298         status.perror("MFnSkinCluster constructor");
02299         return false;
02300       }
02301 
02302       // Get the set of objects that influence the vertices of this
02303       // mesh.  Hopefully these will all be joints.
02304       MDagPathArray influence_objects;
02305       cluster.influenceObjects(influence_objects, &status); 
02306       if (!status) {
02307         status.perror("MFnSkinCluster::influenceObjects");
02308 
02309       } else {
02310         // Fill up the vector with the corresponding table of egg
02311         // groups for each joint.
02312         joints.clear();
02313         for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
02314           MDagPath joint_dag_path = influence_objects[oi];
02315           MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
02316           EggGroup *joint = _tree.get_egg_group(joint_node_desc);
02317           joints.push_back(joint);
02318         }
02319 
02320         // Now use a component object to retrieve all of the weight
02321         // data in one API call.
02322         MFnSingleIndexedComponent sic; 
02323         MObject sic_object = sic.create(MFn::kMeshVertComponent); 
02324         sic.setCompleteData(mesh.numVertices()); 
02325         unsigned influence_count; 
02326 
02327         status = cluster.getWeights(dag_path, sic_object, 
02328                                     weights, influence_count); 
02329         if (!status) {
02330           status.perror("MFnSkinCluster::getWeights");
02331         } else {
02332           if (influence_count != influence_objects.length()) {
02333             mayaegg_cat.error()
02334               << "MFnSkinCluster::influenceObjects() returns " 
02335               << influence_objects.length()
02336               << " objects, but MFnSkinCluster::getWeights() reports "
02337               << influence_count << " objects.\n";
02338             
02339           } else {
02340             // We've got the weights and the set of objects.  That's all
02341             // we need.
02342             return true;
02343           }
02344         }
02345       }
02346     } else if (c_node.hasFn(MFn::kWeightGeometryFilt)) {
02347       // We've found the joint cluster handle. (rigid Binding)
02348       // 
02349       MFnWeightGeometryFilter cluster(c_node, &status); 
02350       if (!status) {
02351         status.perror("MFnWeightGeometryFilter constructor");
02352         return false;
02353       }
02354 
02355       MPlug matrix_plug = cluster.findPlug("matrix");
02356       if (!matrix_plug.isNull()) {
02357         MPlugArray matrix_pa;
02358         matrix_plug.connectedTo(matrix_pa, true, false, &status);
02359         if (!status) {
02360           status.perror("Can't find connected Joint");
02361         } else {
02362           MObject jointObj = matrix_pa[0].node();
02363           MFnIkJoint jointFn(jointObj, &status);
02364           if (!status) {
02365             status.perror("Can't find connected JointDag");
02366           } else {
02367             joints.clear();
02368             MDagPath joint_dag_path = MDagPath();
02369             status = jointFn.getPath(joint_dag_path);
02370             if (!status) {
02371               status.perror("MFnIkJoint::dagPath");
02372             } else {
02373               MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
02374               EggGroup *joint = _tree.get_egg_group(joint_node_desc);
02375               joints.push_back(joint);
02376           
02377               // Now use a component object to retrieve all of the weight
02378               // data in one API call.
02379               MFnSingleIndexedComponent sic; 
02380               MObject sic_object = sic.create(MFn::kMeshVertComponent); 
02381               sic.setCompleteData(mesh.numVertices()); 
02382           
02383               status = cluster.getWeights(dag_path, sic_object, 
02384                                           weights); 
02385               if (!status) {
02386                 status.perror("MFnWeightGeometryFilter::getWeights");
02387               } else {
02388                 // We've got the weights and the set of objects.  That's all
02389                 // we need.
02390                 return true;
02391               }
02392             }
02393           }
02394         }
02395       }      
02396     }
02397 
02398     it.next();
02399   }
02400 
02401   // The mesh was not soft-skinned.
02402   return false;
02403 }
02404 
02405 ////////////////////////////////////////////////////////////////////
02406 //     Function: MayaToEggConverter::get_vertex_weights
02407 //       Access: Private
02408 //  Description: As above, for a NURBS surface instead of a polygon
02409 //               mesh.
02410 ////////////////////////////////////////////////////////////////////
02411 bool MayaToEggConverter::
02412 get_vertex_weights(const MDagPath &dag_path, const MFnNurbsSurface &surface,
02413                    pvector<EggGroup *> &joints, MFloatArray &weights) {
02414   MStatus status;
02415   
02416   // Since we are working with a NURBS surface the input attribute that 
02417   // creates the surface is named "create" 
02418   // 
02419   MObject attr = surface.attribute("create"); 
02420   
02421   // Create the plug to the "create" attribute then use the 
02422   // DG iterator to walk through the DG, at the node level.
02423   // 
02424   MPlug history(surface.object(), attr); 
02425   MItDependencyGraph it(history, MFn::kDependencyNode, 
02426                         MItDependencyGraph::kUpstream, 
02427                         MItDependencyGraph::kDepthFirst, 
02428                         MItDependencyGraph::kNodeLevel);
02429 
02430   while (!it.isDone()) {
02431     // We will walk along the node level of the DG until we 
02432     // spot a skinCluster node.
02433     // 
02434     MObject c_node = it.thisNode(); 
02435     if (c_node.hasFn(MFn::kSkinClusterFilter)) { 
02436       // We've found the cluster handle. Try to get the weight
02437       // data.
02438       // 
02439       MFnSkinCluster cluster(c_node, &status); 
02440       if (!status) {
02441         status.perror("MFnSkinCluster constructor");
02442         return false;
02443       }
02444 
02445       // Get the set of objects that influence the vertices of this
02446       // surface.  Hopefully these will all be joints.
02447       MDagPathArray influence_objects;
02448       cluster.influenceObjects(influence_objects, &status); 
02449       if (!status) {
02450         status.perror("MFnSkinCluster::influenceObjects");
02451 
02452       } else {
02453         // Fill up the vector with the corresponding table of egg
02454         // groups for each joint.
02455         joints.clear();
02456         for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
02457           MDagPath joint_dag_path = influence_objects[oi];
02458           MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
02459           EggGroup *joint = _tree.get_egg_group(joint_node_desc);
02460           joints.push_back(joint);
02461         }
02462 
02463         // Now use a component object to retrieve all of the weight
02464         // data in one API call.
02465         MFnDoubleIndexedComponent dic; 
02466         MObject dic_object = dic.create(MFn::kSurfaceCVComponent); 
02467         dic.setCompleteData(surface.numCVsInU(), surface.numCVsInV()); 
02468         unsigned influence_count; 
02469 
02470         status = cluster.getWeights(dag_path, dic_object, 
02471                                     weights, influence_count); 
02472         if (!status) {
02473           status.perror("MFnSkinCluster::getWeights");
02474         } else {
02475           if (influence_count != influence_objects.length()) {
02476             mayaegg_cat.error()
02477               << "MFnSkinCluster::influenceObjects() returns " 
02478               << influence_objects.length()
02479               << " objects, but MFnSkinCluster::getWeights() reports "
02480               << influence_count << " objects.\n";
02481             
02482           } else {
02483             // We've got the weights and the set of objects.  That's all
02484             // we need.
02485             return true;
02486           }
02487         }
02488       }
02489     }
02490 
02491     it.next();
02492   }
02493 
02494   // The surface was not soft-skinned.
02495   return false;
02496 }
02497 
02498 ////////////////////////////////////////////////////////////////////
02499 //     Function: MayaShader::set_shader_attributes
02500 //       Access: Private
02501 //  Description: Applies the known shader attributes to the indicated
02502 //               egg primitive. Note: For multi-textures, Maya lists 
02503 //               the top most texture in slot 0. But Panda puts the 
02504 //               base texture at slot 0. Hence I parse the list of 
02505 //               textures from last to first.
02506 ////////////////////////////////////////////////////////////////////
02507 void MayaToEggConverter::
02508 set_shader_attributes(EggPrimitive &primitive, const MayaShader &shader,
02509                       bool mesh) {
02510   if (shader._legacy_mode) {
02511     set_shader_legacy(primitive, shader, mesh);
02512   } else {
02513     set_shader_modern(primitive, shader, mesh);
02514   }
02515 }
02516 
02517 ////////////////////////////////////////////////////////////////////
02518 //     Function: MayaToEggConverter::set_shader_modern
02519 //       Access: Private
02520 //  Description: The modern implementation of set_shader_attributes.
02521 //
02522 //               In the modern codepath, the MayaShader is a direct,
02523 //               literal representation of a list of EggTextures.
02524 //               All this exporter has to do is translate the list
02525 //               without interpretation.  All the complex interpretation
02526 //               is handled elsewhere, in the MayaShader module.
02527 ////////////////////////////////////////////////////////////////////
02528 void MayaToEggConverter::
02529 set_shader_modern(EggPrimitive &primitive, const MayaShader &shader,
02530                       bool mesh) {
02531   
02532   for (size_t idx=0; idx < shader._all_maps.size(); idx++) {
02533     MayaShaderColorDef *def = shader._all_maps[idx];
02534     if ((def->_is_alpha)&&(def->_opposite != 0)) {
02535       // This texture represents an alpha-filename.  It doesn't get its own <Texture>
02536       continue;
02537     }
02538     
02539     EggTexture tex(shader.get_name(), "");
02540     tex.set_format(def->_is_alpha ? EggTexture::F_alpha : EggTexture::F_rgb);
02541     apply_texture_filename(tex, *def);
02542     if (def->_opposite) {
02543       apply_texture_alpha_filename(tex, *def);
02544     }
02545     apply_texture_uvprops(tex, *def);
02546     apply_texture_blendtype(tex, *def);
02547     tex.set_uv_name(def->get_panda_uvset_name());
02548   
02549     EggTexture *new_tex =
02550       _textures.create_unique_texture(tex, ~0);
02551     primitive.add_texture(new_tex);
02552   }
02553 }
02554 
02555 ////////////////////////////////////////////////////////////////////
02556 //     Function: MayaToEggConverter::set_shader_legacy
02557 //       Access: Private
02558 //  Description: The legacy implementation of set_shader_attributes.
02559 //               The old behavior of the exporter is just plain weird.
02560 //               It seems to be a result of an inexperienced coder
02561 //               who made some core mistakes, and then patched them
02562 //               up with kludges.  It seems to produce plausible
02563 //               results in certain specific cases, but overall, it
02564 //               doesn't make any sense.  Unfortunately, this weird
02565 //               behavior cannot be discarded - vast numbers of 3D
02566 //               models have been created that rely on this behavior.
02567 //               The solution is to compartmentalize the weirdness.
02568 //               The legacy codepath, when activated, implements the
02569 //               old weird behavior.  A brand-new codepath that
02570 //               shares almost nothing with the legacy codepath
02571 //               implements a much more straightforward behavior.
02572 ////////////////////////////////////////////////////////////////////
02573 void MayaToEggConverter::
02574 set_shader_legacy(EggPrimitive &primitive, const MayaShader &shader,
02575                       bool mesh) {
02576   
02577   // determine if the base texture or any of the top texture need to be rgb only
02578   MayaShaderColorDef *color_def = NULL;
02579   bool is_rgb = false;
02580   bool is_decal = false;
02581   bool is_interpolate = false;
02582   int i;
02583   // last shader is the base so lets skip it 
02584   for (i=0; i<(int)shader._color.size()-1; ++i) {
02585     color_def = shader.get_color_def(i);
02586     if (color_def->_has_texture) {
02587       if ((EggTexture::EnvType)color_def->_interpolate) {
02588         is_interpolate = true;
02589       }
02590       else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_modulate) {
02591         // Maya's multiply is slightly different than panda's. Unless, _keep_alpha is set, 
02592         // we are dropping the alpha.
02593         if (!color_def->_keep_alpha)
02594           is_rgb = true; // modulate forces the alpha to be ignored
02595       }
02596       else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_decal) {
02597         is_decal = true;
02598       }
02599     }
02600   }
02601 
02602   // we don't want an extra light stage for interpolate mode, it takes care of automatically
02603   if (is_interpolate)
02604     is_decal = false;
02605   
02606   // new decal mode needs an extra dummy layers of textureStage
02607   EggTexture *dummy_tex = (EggTexture *)NULL;
02608   string dummy_uvset_name;
02609 
02610   // In Maya, a polygon is either textured or colored.  The texture,
02611   // if present, replaces the color. Also now there could be multiple textures
02612   const MayaShaderColorDef &trans_def = shader._transparency;
02613   for (i=shader._color.size()-1; i>=0; --i) {
02614     color_def = shader.get_color_def(i);
02615     if (mayaegg_cat.is_spam()) {
02616       mayaegg_cat.spam() << "slot " << i << ":got color_def: " << color_def << endl;
02617     }
02618     if (color_def->_has_texture || trans_def._has_texture) {
02619       EggTexture tex(shader.get_name(), "");
02620       if (mayaegg_cat.is_debug()) {
02621         mayaegg_cat.debug() << "got shader name:" << shader.get_name() << endl;
02622         mayaegg_cat.debug() << "ssa:texture name[" << i << "]: " << color_def->_texture_name << endl;
02623       }
02624 
02625       string uvset_name = _shaders.find_uv_link(color_def->_texture_name);
02626       if (mayaegg_cat.is_debug()) {
02627         mayaegg_cat.debug() << "ssa:corresponding uvset name is " << uvset_name << endl;
02628       }
02629 
02630       if (color_def->_has_texture) {
02631         // If we have a texture on color, apply it as the filename.
02632         //if (mayaegg_cat.is_debug()) {
02633           //mayaegg_cat.debug() << "ssa:got texture name" << color_def->_texture_filename << endl;
02634         //}
02635         Filename filename = Filename::from_os_specific(color_def->_texture_filename);
02636         Filename fullpath, outpath;
02637         _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath);
02638         tex.set_filename(outpath);
02639         tex.set_fullpath(fullpath);
02640         apply_texture_uvprops(tex, *color_def);
02641         
02642         // If we also have a texture on transparency, apply it as the
02643         // alpha filename.
02644         if (trans_def._has_texture) {
02645           if (color_def->_wrap_u != trans_def._wrap_u ||
02646               color_def->_wrap_u != trans_def._wrap_u) {
02647             mayaegg_cat.warning()
02648               << "Shader " << shader.get_name()
02649               << " has contradictory wrap modes on color and texture.\n";
02650           }
02651           
02652           if (!compare_texture_uvprops(tex, trans_def)) {
02653             // Only report each broken shader once.
02654             static pset<string> bad_shaders;
02655             if (bad_shaders.insert(shader.get_name()).second) {
02656               mayaegg_cat.error()
02657                 << "Color and transparency texture properties differ on shader "
02658                 << shader.get_name() << "\n";
02659             }
02660           }
02661           //tex.set_format(EggTexture::F_rgba);
02662           
02663           // We should try to be smarter about whether the transparency
02664           // value is connected to the texture's alpha channel or to its
02665           // grayscale channel.  However, I'm not sure how to detect
02666           // this at the moment; rather than spending days trying to
02667           // figure out, for now I'll just assume that if the same
02668           // texture image is used for both color and transparency, then
02669           // the artist meant to use the alpha channel for transparency.
02670           if (trans_def._texture_filename == color_def->_texture_filename) {
02671             // That means that we don't need to do anything special: use
02672             // all the channels of the texture.
02673             
02674           } else {
02675             // Otherwise, pull the alpha channel from the other image
02676             // file.  Ideally, we should figure out which channel from
02677             // the other image supplies alpha (and specify this via
02678             // set_alpha_file_channel()), but for now we assume it comes
02679             // from the grayscale data.
02680             filename = Filename::from_os_specific(trans_def._texture_filename);
02681             _path_replace->full_convert_path(filename, get_model_path(),
02682                                              fullpath, outpath);
02683             tex.set_alpha_filename(outpath);
02684             tex.set_alpha_fullpath(fullpath);
02685           }
02686         } else {
02687           // If there is no transparency texture specified, we don't
02688           // have any transparency, so tell the egg format to ignore any
02689           // alpha channel that might be on the color texture.
02690           //tex.set_format(EggTexture::F_rgb);
02691         }
02692         
02693         if (shader._color.size() > 1) {
02694           // if multi-textured, first texture in maya is on top, so
02695           // last shader on the list is the base one, which should always pick up the alpha
02696           // from the texture file. But the top textures may have to strip the alpha
02697           if (i!=shader._color.size()-1) {
02698             if (!i && is_interpolate) {
02699               // this is the grass path mode where alpha on this texture determines
02700               // whether to show layer1 or layer2. Since by now other layers are set
02701               // lets change those to get this effect
02702               tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_interpolate);
02703               tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_previous);
02704               tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
02705               tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_last_saved_result);
02706               tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
02707               tex.set_combine_source(EggTexture::CC_rgb, 2, EggTexture::CS_texture);
02708               tex.set_combine_operand(EggTexture::CC_rgb, 2, EggTexture::CO_src_alpha);
02709 
02710               tex.set_combine_mode(EggTexture::CC_alpha, EggTexture::CM_interpolate);
02711               tex.set_combine_source(EggTexture::CC_alpha, 0, EggTexture::CS_previous);
02712               tex.set_combine_operand(EggTexture::CC_alpha, 0, EggTexture::CO_src_alpha);
02713               tex.set_combine_source(EggTexture::CC_alpha, 1, EggTexture::CS_last_saved_result);
02714               tex.set_combine_operand(EggTexture::CC_alpha, 1, EggTexture::CO_src_alpha);
02715               tex.set_combine_source(EggTexture::CC_alpha, 2, EggTexture::CS_texture);
02716               tex.set_combine_operand(EggTexture::CC_alpha, 2, EggTexture::CO_src_alpha);
02717             }
02718             else {
02719               if (is_interpolate) {
02720                 tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate);
02721                 tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color);
02722                 tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
02723                 tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_texture);
02724                 tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
02725               }
02726               else {
02727                 tex.set_env_type((EggTexture::EnvType)color_def->_blend_type);
02728                 if (tex.get_env_type() == EggTexture::ET_modulate) {
02729                   if (color_def->_has_alpha_channel) {
02730                     // lets caution the artist that they should not be using a alpha channel on
02731                     // this texture. 
02732                     if (mayaegg_cat.is_spam()) {
02733                       maya_cat.spam() 
02734                         << color_def->_texture_name 
02735                         << " should not have alpha channel in multiply mode: ignoring\n";
02736                     }
02737                   }
02738                   if (is_rgb) {
02739                     //tex.set_alpha_mode(EggRenderMode::AM_off);  // force alpha off
02740                     tex.set_format(EggTexture::F_rgb);  // Change the format to be rgb only
02741                   }
02742                 }
02743               }
02744             }
02745           }
02746           else {
02747             if (is_interpolate) {
02748               // base shader need to save result
02749               tex.set_saved_result(true);
02750             }
02751             else if (is_decal) {
02752               // decal in classic time, always overwrote the base color. That causes problem
02753               // when the polygon wants to be lit or wants to retain vertex/polygon color
02754               // In the new decal mode, we achieve this with a third dummy layer
02755               // copy this layer to a new dummy layer
02756               EggTexture texDummy(shader.get_name()+".dummy", "");
02757               if (mayaegg_cat.is_debug()) {
02758                 mayaegg_cat.debug() << "creating dummy shader: " << texDummy.get_name() << endl;
02759               }
02760               texDummy.set_filename(outpath);
02761               texDummy.set_fullpath(fullpath);
02762               apply_texture_uvprops(texDummy, *color_def);
02763               texDummy.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate);
02764               texDummy.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color);
02765               texDummy.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
02766               texDummy.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_previous);
02767               texDummy.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
02768               dummy_tex = _textures.create_unique_texture(texDummy, ~0);
02769 
02770               // make this layer ET_replace
02771               tex.set_env_type(EggTexture::ET_replace);
02772             }
02773           }
02774         }
02775       } else {  // trans_def._has_texture
02776         // We have a texture on transparency only.  Apply it as the
02777         // primary filename, and set the format accordingly.
02778         Filename filename = Filename::from_os_specific(trans_def._texture_filename);
02779         Filename fullpath,outpath;
02780         _path_replace->full_convert_path(filename, get_model_path(),
02781                                          fullpath, outpath);
02782         tex.set_filename(outpath);
02783         tex.set_fullpath(fullpath);
02784         tex.set_format(EggTexture::F_alpha);
02785         apply_texture_uvprops(tex, trans_def);
02786       }
02787       
02788       if (mayaegg_cat.is_debug()) {
02789         mayaegg_cat.debug() << "ssa:tref_name:" << tex.get_name() << endl;
02790       }
02791       if (is_rgb && i == (int)shader._color.size()-1) {
02792         // make base layer rgb only
02793         tex.set_format(EggTexture::F_rgb);  // Change the format to be rgb only
02794       }
02795       EggTexture *new_tex =
02796         _textures.create_unique_texture(tex, ~0);
02797       
02798       if (mesh) {
02799         if (uvset_name.find("not found") == -1) {
02800           primitive.add_texture(new_tex);
02801           color_def->_uvset_name.assign(uvset_name.c_str());
02802           if (uvset_name != "map1") {
02803             new_tex->set_uv_name(uvset_name);
02804           }
02805           if (i == (int)shader._color.size()-1 && is_decal) {
02806             dummy_uvset_name.assign(color_def->_uvset_name);
02807           }
02808         }
02809       } else {
02810         primitive.add_texture(new_tex);
02811         if (color_def->_uvset_name != "map1") {
02812           new_tex->set_uv_name(color_def->_uvset_name);
02813         }
02814       }
02815     }
02816   }
02817   if (dummy_tex != (EggTexture *)NULL) {
02818     primitive.add_texture(dummy_tex);
02819     dummy_tex->set_uv_name(dummy_uvset_name);
02820   }
02821   // Also apply an overall color to the primitive.
02822   LColor rgba = shader.get_rgba();
02823   if (mayaegg_cat.is_spam()) {
02824     mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl;
02825   }
02826 
02827   // The existence of a texture on either color channel completely
02828   // replaces the corresponding flat color.
02829   if (color_def && color_def->_has_texture) {
02830     rgba[0] = 1.0f;
02831     rgba[1] = 1.0f;
02832     rgba[2] = 1.0f;
02833   }
02834   if (trans_def._has_texture) {
02835     rgba[3] = 1.0f;
02836   }
02837 
02838   // But the color gain always gets applied.
02839   if (color_def) {
02840     rgba[0] *= color_def->_color_gain[0];
02841     rgba[1] *= color_def->_color_gain[1];
02842     rgba[2] *= color_def->_color_gain[2];
02843     rgba[3] *= color_def->_color_gain[3];
02844     if (mayaegg_cat.is_spam()) {
02845       mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl;
02846     }
02847   }
02848 
02849   primitive.set_color(rgba);
02850 
02851   if (mayaegg_cat.is_spam()) {
02852     mayaegg_cat.spam() << "  set_shader_attributes : end\n";
02853   }
02854 }
02855 
02856 ////////////////////////////////////////////////////////////////////
02857 //     Function: MayaShader::apply_texture_uvprops
02858 //       Access: Private
02859 //  Description: Applies all the appropriate texture properties to the
02860 //               EggTexture object, including wrap modes and texture
02861 //               matrix.
02862 ////////////////////////////////////////////////////////////////////
02863 void MayaToEggConverter::
02864 apply_texture_uvprops(EggTexture &tex, const MayaShaderColorDef &color_def) {
02865   // Let's mipmap all textures by default.
02866   tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
02867   tex.set_magfilter(EggTexture::FT_linear);
02868 
02869   EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
02870   EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
02871 
02872   tex.set_wrap_u(wrap_u);
02873   tex.set_wrap_v(wrap_v);
02874 
02875   LMatrix3d mat = color_def.compute_texture_matrix();
02876   if (!mat.almost_equal(LMatrix3d::ident_mat())) {
02877     tex.set_transform2d(mat);
02878   }
02879 }
02880 
02881 ////////////////////////////////////////////////////////////////////
02882 //     Function: MayaShader::apply_texture_blendtype
02883 //       Access: Private
02884 //  Description: Applies the blendtype to the EggTexture.
02885 ////////////////////////////////////////////////////////////////////
02886 void MayaToEggConverter::
02887 apply_texture_blendtype(EggTexture &tex, const MayaShaderColorDef &color_def) {
02888   switch (color_def._blend_type) {
02889   case MayaShaderColorDef::BT_unspecified:
02890     tex.set_env_type(EggTexture::ET_unspecified);
02891     return;
02892   case MayaShaderColorDef::BT_modulate:
02893     tex.set_env_type(EggTexture::ET_modulate);
02894     return;
02895   case MayaShaderColorDef::BT_decal:
02896     tex.set_env_type(EggTexture::ET_decal);
02897     return;
02898   case MayaShaderColorDef::BT_blend:
02899     tex.set_env_type(EggTexture::ET_blend);
02900     return;
02901   case MayaShaderColorDef::BT_replace:
02902     tex.set_env_type(EggTexture::ET_replace);
02903     return;
02904   case MayaShaderColorDef::BT_add:
02905     tex.set_env_type(EggTexture::ET_add);
02906     return;
02907   case MayaShaderColorDef::BT_blend_color_scale:
02908     tex.set_env_type(EggTexture::ET_blend_color_scale);
02909     return;
02910   case MayaShaderColorDef::BT_modulate_glow:
02911     tex.set_env_type(EggTexture::ET_modulate_glow);
02912     return;
02913   case MayaShaderColorDef::BT_modulate_gloss:
02914     tex.set_env_type(EggTexture::ET_modulate_gloss);
02915     return;
02916   case MayaShaderColorDef::BT_normal:
02917     tex.set_env_type(EggTexture::ET_normal);
02918     return;
02919   case MayaShaderColorDef::BT_normal_height:
02920     tex.set_env_type(EggTexture::ET_normal_height);
02921     return;
02922   case MayaShaderColorDef::BT_glow:
02923     tex.set_env_type(EggTexture::ET_glow);
02924     return;
02925   case MayaShaderColorDef::BT_gloss:
02926     tex.set_env_type(EggTexture::ET_gloss);
02927     return;
02928   case MayaShaderColorDef::BT_height:
02929     tex.set_env_type(EggTexture::ET_height);
02930     return;
02931   case MayaShaderColorDef::BT_selector:
02932     tex.set_env_type(EggTexture::ET_selector);
02933     return;
02934   }
02935 }
02936 
02937 ////////////////////////////////////////////////////////////////////
02938 //     Function: MayaShader::apply_texture_filename
02939 //       Access: Private
02940 //  Description: Applies the filename to the EggTexture.
02941 ////////////////////////////////////////////////////////////////////
02942 void MayaToEggConverter::
02943 apply_texture_filename(EggTexture &tex, const MayaShaderColorDef &def) {
02944   Filename filename = Filename::from_os_specific(def._texture_filename);
02945   Filename fullpath, outpath;
02946   _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath);
02947   tex.set_filename(outpath);
02948   tex.set_fullpath(fullpath);
02949 }
02950 
02951 ////////////////////////////////////////////////////////////////////
02952 //     Function: MayaShader::apply_texture_alpha_filename
02953 //       Access: Private
02954 //  Description: Applies the alpha filename to the EggTexture.
02955 ////////////////////////////////////////////////////////////////////
02956 void MayaToEggConverter::
02957 apply_texture_alpha_filename(EggTexture &tex, const MayaShaderColorDef &def) {
02958   if (def._opposite) {
02959     tex.set_format(EggTexture::F_rgba);
02960     if (def._opposite->_texture_filename != def._texture_filename) {
02961       Filename filename = Filename::from_os_specific(def._opposite->_texture_filename);
02962       Filename fullpath, outpath;
02963       _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath);
02964       tex.set_alpha_filename(outpath);
02965       tex.set_alpha_fullpath(fullpath);
02966     }
02967   }
02968 }
02969 
02970 ////////////////////////////////////////////////////////////////////
02971 //     Function: MayaShader::compare_texture_uvprops
02972 //       Access: Private
02973 //  Description: Compares the texture properties already on the
02974 //               texture (presumably set by a previous call to
02975 //               apply_texture_uvprops()) and returns false if they
02976 //               differ from that specified by the indicated color_def
02977 //               object, or true if they match.
02978 ////////////////////////////////////////////////////////////////////
02979 bool MayaToEggConverter::
02980 compare_texture_uvprops(EggTexture &tex, 
02981                         const MayaShaderColorDef &color_def) {
02982   bool okflag = true;
02983 
02984   EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
02985   EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
02986   
02987   if (wrap_u != tex.determine_wrap_u()) {
02988     // Choose the more general of the two.
02989     if (wrap_u == EggTexture::WM_repeat) {
02990       tex.set_wrap_u(wrap_u);
02991     }
02992     okflag = false;
02993   }
02994   if (wrap_v != tex.determine_wrap_v()) {
02995     if (wrap_v == EggTexture::WM_repeat) {
02996       tex.set_wrap_v(wrap_v);
02997     }
02998     okflag = false;
02999   }
03000   
03001   LMatrix3d m = color_def.compute_texture_matrix();
03002   LMatrix4d mat4(m(0, 0), m(0, 1), 0.0, m(0, 2),
03003                  m(1, 0), m(1, 1), 0.0, m(1, 2),
03004                  0.0, 0.0, 1.0, 0.0,
03005                  m(2, 0), m(2, 1), 0.0, m(2, 2));
03006   if (!mat4.almost_equal(tex.get_transform3d())) {
03007     okflag = false;
03008   }
03009 
03010   return okflag;
03011 }
03012 
03013 ////////////////////////////////////////////////////////////////////
03014 //     Function: MayaShader::reparent_decals
03015 //       Access: Private
03016 //  Description: Recursively walks the egg hierarchy, reparenting
03017 //               "decal" type nodes below their corresponding
03018 //               "decalbase" type nodes, and setting the flags.
03019 //
03020 //               Returns true on success, false if some nodes were
03021 //               incorrect.
03022 ////////////////////////////////////////////////////////////////////
03023 bool MayaToEggConverter::
03024 reparent_decals(EggGroupNode *egg_parent) {
03025   bool okflag = true;
03026 
03027   // First, walk through all children of this node, looking for the
03028   // one decal base, if any.
03029   EggGroup *decal_base = (EggGroup *)NULL;
03030   pvector<EggGroup *> decal_children;
03031 
03032   EggGroupNode::iterator ci;
03033   for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
03034     EggNode *child =  (*ci);
03035     if (child->is_of_type(EggGroup::get_class_type())) {
03036       EggGroup *child_group = DCAST(EggGroup, child);
03037       if (child_group->has_object_type("decalbase")) {
03038         if (decal_base != (EggNode *)NULL) {
03039           mayaegg_cat.error()
03040             << "Two children of " << egg_parent->get_name()
03041             << " both have decalbase set: " << decal_base->get_name()
03042             << " and " << child_group->get_name() << "\n";
03043           okflag = false;
03044         }
03045         child_group->remove_object_type("decalbase");
03046         decal_base = child_group;
03047 
03048       } else if (child_group->has_object_type("decal")) {
03049         child_group->remove_object_type("decal");
03050         decal_children.push_back(child_group);
03051       }
03052     }
03053   }
03054 
03055   if (decal_base == (EggGroup *)NULL) {
03056     if (!decal_children.empty()) {
03057       mayaegg_cat.warning()
03058         << decal_children.front()->get_name()
03059         << " has decal, but no sibling node has decalbase.\n";
03060     }
03061 
03062   } else {
03063     if (decal_children.empty()) {
03064       mayaegg_cat.warning()
03065         << decal_base->get_name()
03066         << " has decalbase, but no sibling nodes have decal.\n";
03067 
03068     } else {
03069       // All the decal children get moved to be a child of decal base.
03070       // This usually will not affect the vertex positions, but it
03071       // could if the decal base has a transform and the decal child
03072       // is an instance node.  So don't do that.
03073       pvector<EggGroup *>::iterator di;
03074       for (di = decal_children.begin(); di != decal_children.end(); ++di) {
03075         EggGroup *child_group = (*di);
03076         decal_base->add_child(child_group);
03077       }
03078 
03079       // Also set the decal state on the base.
03080       decal_base->set_decal_flag(true);
03081     }
03082   }
03083 
03084   // Now recurse on each of the child nodes.
03085   for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
03086     EggNode *child =  (*ci);
03087     if (child->is_of_type(EggGroupNode::get_class_type())) {
03088       EggGroupNode *child_group = DCAST(EggGroupNode, child);
03089       if (!reparent_decals(child_group)) {
03090         okflag = false;
03091       }
03092     }
03093   }
03094 
03095   return okflag;
03096 }
03097 
03098 ////////////////////////////////////////////////////////////////////
03099 //     Function: MayaShader::string_transform_type
03100 //       Access: Public, Static
03101 //  Description: Returns the TransformType value corresponding to the
03102 //               indicated string, or TT_invalid.
03103 ////////////////////////////////////////////////////////////////////
03104 MayaToEggConverter::TransformType MayaToEggConverter::
03105 string_transform_type(const string &arg) {
03106   if (cmp_nocase(arg, "all") == 0) {
03107     return TT_all;
03108   } else if (cmp_nocase(arg, "model") == 0) {
03109     return TT_model;
03110   } else if (cmp_nocase(arg, "dcs") == 0) {
03111     return TT_dcs;
03112   } else if (cmp_nocase(arg, "none") == 0) {
03113     return TT_none;
03114   } else {
03115     return TT_invalid;
03116   }
03117 }
03118 ////////////////////////////////////////////////////////////////////
03119 //     Function: MayaShader::MayaToEggConverter::set_vertex_color
03120 //       Access: Private
03121 //  Description: Checks to see if we're using legacy or modern
03122 //               shaders and based on the result, it passes
03123 //               the vertex color calculations off to either
03124 //               the legacy or modern vertex color functions.
03125 ////////////////////////////////////////////////////////////////////
03126 void MayaToEggConverter::
03127 set_vertex_color(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const LColor &color) {
03128     if (shader == (MayaShader *)NULL || shader->_legacy_mode) {
03129       set_vertex_color_legacy(vert, pi, vert_index, shader, color);
03130     } else {
03131       set_vertex_color_modern(vert, pi, vert_index, shader, color);
03132     }
03133 }
03134 ////////////////////////////////////////////////////////////////////
03135 //     Function: MayaShader::MayaToEggConverter::set_vertex_color_legacy
03136 //       Access: Private
03137 //  Description: Calls set_color on an EggVertex, determining the
03138 //               correct color values, based on the shader, vert_color
03139 //               Maya's vertex & flat color(s).  This is the original 
03140 //               implementation that works only on Lambert shaders/materials
03141 //               in Maya.
03142 ////////////////////////////////////////////////////////////////////
03143 void MayaToEggConverter::
03144 set_vertex_color_legacy(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const LColor &color){
03145   if (pi.hasColor()) {
03146     MColor c;
03147     MStatus status = pi.getColor(c, vert_index);
03148     if (!status) {
03149       status.perror("MItMeshPolygon::getColor");
03150     } else {
03151       // I saw instances where the color components exceeded 1.0
03152       // so lets clamp the values to 0 to 1
03153       c /= 1.0;
03154       // The vertex color is a color scale that modifies the
03155       // polygon color, not an override that replaces it.
03156       vert.set_color(LColor(c.r * color[0], c.g * color[1], c.b * color[2], c.a * color[3]));
03157 
03158       if (mayaegg_cat.is_spam()) {
03159         mayaegg_cat.spam() << "maya_color = " << c.r << " " << c.g << " " << c.b << " " << c.a << endl;
03160         mayaegg_cat.spam() << "vert_color = " << vert.get_color() << endl;
03161       }
03162     }
03163   } else {
03164     vert.set_color(color);
03165   }
03166 
03167 }
03168 ////////////////////////////////////////////////////////////////////
03169 //     Function: MayaShader::MayaToEggConverter::set_vertex_color_modern
03170 //       Access: Private
03171 //  Description: Calls set_color on an EggVertex, determining the
03172 //               correct color values, based on the shader, vert_color
03173 //               Maya's vertex & flat color(s).  This implementation
03174 //               is designed to work specifically with Phong materials
03175 //               or shaders.
03176 ////////////////////////////////////////////////////////////////////
03177 void MayaToEggConverter::
03178 set_vertex_color_modern(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const LColor &color) {
03179   // If there's an explicit vertex color, output it.
03180   if (pi.hasColor(vert_index)) {
03181     MColor c;
03182     MStatus status = pi.getColor(c, vert_index);
03183     if (status) {
03184       vert.set_color(LColor(c.r, c.g, c.b, c.a));
03185       return;
03186     }
03187   }
03188   
03189   // If there's no explicit color, use flat color, or white on a textured model.
03190   if (shader->_color_maps.empty()) {
03191     const LColord &c = shader->_flat_color;
03192     vert.set_color(LColor((PN_stdfloat)c[0], (PN_stdfloat)c[1], (PN_stdfloat)c[2], (PN_stdfloat)c[3]));
03193   } else {
03194     //there's no explicit color anywhere, must be textured (or blank)
03195     vert.set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
03196   }
03197 }
 All Classes Functions Variables Enumerations