Panda3D

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