Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/mayaegg/mayaToEggConverter.cxx

Go to the documentation of this file.
00001 // Filename: mayaToEggConverter.cxx
00002 // Created by:  drose (10Nov99)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "mayaToEggConverter.h"
00020 #include "mayaShader.h"
00021 #include "maya_funcs.h"
00022 #include "config_mayaegg.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 "string_utils.h"
00037 #include "dcast.h"
00038 
00039 #include "pre_maya_include.h"
00040 #include <maya/MArgList.h>
00041 #include <maya/MColor.h>
00042 #include <maya/MDagPath.h>
00043 #include <maya/MFnCamera.h>
00044 #include <maya/MFnDagNode.h>
00045 #include <maya/MFnTransform.h>
00046 #include <maya/MFnLight.h>
00047 #include <maya/MFnNurbsSurface.h>
00048 #include <maya/MFnNurbsCurve.h>
00049 #include <maya/MFnMesh.h>
00050 #include <maya/MFnMeshData.h>
00051 #include <maya/MItMeshPolygon.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/MFnSingleIndexedComponent.h>
00072 #include <maya/MItDependencyGraph.h>
00073 #include <maya/MDagPathArray.h>
00074 #include <maya/MSelectionList.h>
00075 #include "post_maya_include.h"
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: MayaToEggConverter::Constructor
00079 //       Access: Public
00080 //  Description: 
00081 ////////////////////////////////////////////////////////////////////
00082 MayaToEggConverter::
00083 MayaToEggConverter(const string &program_name) :
00084   _program_name(program_name)
00085 {
00086   _from_selection = false;
00087   _polygon_output = false;
00088   _polygon_tolerance = 0.01;
00089   _transform_type = TT_model;
00090 }
00091 
00092 ////////////////////////////////////////////////////////////////////
00093 //     Function: MayaToEggConverter::Copy Constructor
00094 //       Access: Public
00095 //  Description: 
00096 ////////////////////////////////////////////////////////////////////
00097 MayaToEggConverter::
00098 MayaToEggConverter(const MayaToEggConverter &copy) :
00099   _maya(copy._maya)
00100 {
00101 }
00102 
00103 ////////////////////////////////////////////////////////////////////
00104 //     Function: MayaToEggConverter::Destructor
00105 //       Access: Public, Virtual
00106 //  Description: 
00107 ////////////////////////////////////////////////////////////////////
00108 MayaToEggConverter::
00109 ~MayaToEggConverter() {
00110   close_api();
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //     Function: MayaToEggConverter::make_copy
00115 //       Access: Public, Virtual
00116 //  Description: Allocates and returns a new copy of the converter.
00117 ////////////////////////////////////////////////////////////////////
00118 SomethingToEggConverter *MayaToEggConverter::
00119 make_copy() {
00120   return new MayaToEggConverter(*this);
00121 }
00122 
00123 ////////////////////////////////////////////////////////////////////
00124 //     Function: MayaToEggConverter::get_name
00125 //       Access: Public, Virtual
00126 //  Description: Returns the English name of the file type this
00127 //               converter supports.
00128 ////////////////////////////////////////////////////////////////////
00129 string MayaToEggConverter::
00130 get_name() const {
00131   return "Maya";
00132 }
00133 
00134 ////////////////////////////////////////////////////////////////////
00135 //     Function: MayaToEggConverter::get_extension
00136 //       Access: Public, Virtual
00137 //  Description: Returns the common extension of the file type this
00138 //               converter supports.
00139 ////////////////////////////////////////////////////////////////////
00140 string MayaToEggConverter::
00141 get_extension() const {
00142   return "mb";
00143 }
00144 
00145 ////////////////////////////////////////////////////////////////////
00146 //     Function: MayaToEggConverter::convert_file
00147 //       Access: Public, Virtual
00148 //  Description: Handles the reading of the input file and converting
00149 //               it to egg.  Returns true if successful, false
00150 //               otherwise.
00151 //
00152 //               This is designed to be as generic as possible,
00153 //               generally in support of run-time loading.
00154 //               Also see convert_maya().
00155 ////////////////////////////////////////////////////////////////////
00156 bool MayaToEggConverter::
00157 convert_file(const Filename &filename) {
00158   if (!open_api()) {
00159     mayaegg_cat.error()
00160       << "Maya is not available.\n";
00161     return false;
00162   }
00163   if (!_maya->read(filename)) {
00164     mayaegg_cat.error()
00165       << "Unable to read " << filename << "\n";
00166     return false;
00167   }
00168 
00169   if (_character_name.empty()) {
00170     _character_name = filename.get_basename_wo_extension();
00171   }
00172 
00173   return convert_maya(false);
00174 }
00175 
00176 ////////////////////////////////////////////////////////////////////
00177 //     Function: MayaToEggConverter::convert_maya
00178 //       Access: Public
00179 //  Description: Fills up the egg_data structure according to the
00180 //               global maya model data.  Returns true if successful,
00181 //               false if there is an error.  If from_selection is
00182 //               true, the converted geometry is based on that which
00183 //               is selected; otherwise, it is the entire Maya scene.
00184 ////////////////////////////////////////////////////////////////////
00185 bool MayaToEggConverter::
00186 convert_maya(bool from_selection) {
00187   _from_selection = from_selection;
00188   _textures.clear();
00189   _shaders.clear();
00190   _groups.clear();
00191   _tables.clear();
00192 
00193   if (!open_api()) {
00194     mayaegg_cat.error()
00195       << "Maya is not available.\n";
00196     return false;
00197   }
00198 
00199   if (_egg_data->get_coordinate_system() == CS_default) {
00200     _egg_data->set_coordinate_system(_maya->get_coordinate_system());
00201   }
00202 
00203   mayaegg_cat.info()
00204     << "Converting from Maya.\n";
00205 
00206   // Figure out the animation parameters.
00207   double start_frame, end_frame, frame_inc, input_frame_rate, output_frame_rate;
00208   if (has_start_frame()) {
00209     start_frame = get_start_frame();
00210   } else {
00211     start_frame = MAnimControl::minTime().value();
00212   }
00213   if (has_end_frame()) {
00214     end_frame = get_end_frame();
00215   } else {
00216     end_frame = MAnimControl::maxTime().value();
00217   }
00218   if (has_frame_inc()) {
00219     frame_inc = get_frame_inc();
00220   } else {
00221     frame_inc = 1.0;
00222   }
00223   if (has_input_frame_rate()) {
00224     input_frame_rate = get_input_frame_rate();
00225   } else {
00226     MTime time(1.0, MTime::kSeconds);
00227     input_frame_rate = time.as(MTime::uiUnit());
00228   }
00229   if (has_output_frame_rate()) {
00230     output_frame_rate = get_output_frame_rate();
00231   } else {
00232     output_frame_rate = input_frame_rate;
00233   }
00234 
00235   bool all_ok = true;
00236 
00237   switch (get_animation_convert()) {
00238   case AC_pose:
00239     // pose: set to a specific frame, then get out the static geometry.
00240     mayaegg_cat.info(false)
00241       << "frame " << start_frame << "\n";
00242     MGlobal::viewFrame(MTime(start_frame, MTime::uiUnit()));
00243     // fall through
00244 
00245   case AC_none:
00246     // none: just get out a static model, no animation.
00247     all_ok = convert_hierarchy(&get_egg_data());
00248     break;
00249 
00250   case AC_flip:
00251     // flip: get out a series of static models, one per frame, under a
00252     // sequence node.
00253     all_ok = convert_flip(start_frame, end_frame, frame_inc,
00254                           output_frame_rate);
00255     break;
00256 
00257   case AC_model:
00258     // model: get out an animatable model with joints and vertex
00259     // membership.
00260     all_ok = convert_char_model();
00261     break;
00262 
00263   case AC_chan:
00264     // chan: get out a series of animation tables.
00265     all_ok = convert_char_chan(start_frame, end_frame, frame_inc,
00266                                output_frame_rate);
00267     break;
00268 
00269   case AC_both:
00270     // both: Put a model and its animation into the same egg file.
00271     _animation_convert = AC_model;
00272     if (!convert_char_model()) {
00273       all_ok = false;
00274     }
00275     _animation_convert = AC_chan;
00276     if (!convert_char_chan(start_frame, end_frame, frame_inc,
00277                            output_frame_rate)) {
00278       all_ok = false;
00279     }
00280     break;
00281   };
00282 
00283   reparent_decals(&get_egg_data());
00284 
00285   if (all_ok) {
00286     mayaegg_cat.info()
00287       << "Converted, no errors.\n";
00288   } else {
00289     mayaegg_cat.info()
00290       << "Errors encountered in conversion.\n";
00291   }
00292 
00293   return all_ok;
00294 }
00295 
00296 ////////////////////////////////////////////////////////////////////
00297 //     Function: MayaToEggConverter::open_api
00298 //       Access: Public
00299 //  Description: Attempts to open the Maya API if it was not already
00300 //               open, and returns true if successful, or false if
00301 //               there is an error.
00302 ////////////////////////////////////////////////////////////////////
00303 bool MayaToEggConverter::
00304 open_api() {
00305   if (_maya == (MayaApi *)NULL || !_maya->is_valid()) {
00306     _maya = MayaApi::open_api(_program_name);
00307   }
00308   return _maya->is_valid();
00309 }
00310 
00311 ////////////////////////////////////////////////////////////////////
00312 //     Function: MayaToEggConverter::close_api
00313 //       Access: Public
00314 //  Description: Closes the Maya API, if it was previously opened.
00315 //               Caution!  Maya appears to call exit() when its API is
00316 //               closed.
00317 ////////////////////////////////////////////////////////////////////
00318 void MayaToEggConverter::
00319 close_api() {
00320   // We have to clear the shaders before we release the Maya API.
00321   _shaders.clear();
00322   _maya.clear();
00323 }
00324 
00325 ////////////////////////////////////////////////////////////////////
00326 //     Function: MayaToEggConverter::convert_flip
00327 //       Access: Private
00328 //  Description: Converts the animation as a series of models that
00329 //               cycle (flip) from one to the next at the appropriate
00330 //               frame rate.  This is the most likely to convert
00331 //               precisely (since we ask Maya to tell us the vertex
00332 //               position each time) but it is the most wasteful in
00333 //               terms of memory utilization (since a complete of the
00334 //               model is stored for each frame).
00335 ////////////////////////////////////////////////////////////////////
00336 bool MayaToEggConverter::
00337 convert_flip(double start_frame, double end_frame, double frame_inc,
00338              double output_frame_rate) {
00339   bool all_ok = true;
00340 
00341   EggGroup *sequence_node = new EggGroup(_character_name);
00342   get_egg_data().add_child(sequence_node);
00343   sequence_node->set_switch_flag(true);
00344   sequence_node->set_switch_fps(output_frame_rate / frame_inc);
00345 
00346   MTime frame(start_frame, MTime::uiUnit());
00347   MTime frame_stop(end_frame, MTime::uiUnit());
00348   while (frame <= frame_stop) {
00349     mayaegg_cat.info(false)
00350       << "frame " << frame.value() << "\n";
00351     ostringstream name_strm;
00352     name_strm << "frame" << frame.value();
00353     EggGroup *frame_root = new EggGroup(name_strm.str());
00354     sequence_node->add_child(frame_root);
00355 
00356     MGlobal::viewFrame(frame);
00357     if (!convert_hierarchy(frame_root)) {
00358       all_ok = false;
00359     }
00360     _groups.clear();
00361 
00362     frame += frame_inc;
00363   }
00364 
00365   return all_ok;
00366 }
00367 
00368 ////////////////////////////////////////////////////////////////////
00369 //     Function: MayaToEggConverter::convert_char_model
00370 //       Access: Private
00371 //  Description: Converts the file as an animatable character
00372 //               model, with joints and vertex membership.
00373 ////////////////////////////////////////////////////////////////////
00374 bool MayaToEggConverter::
00375 convert_char_model() {
00376   if (has_neutral_frame()) {
00377     MTime frame(get_neutral_frame(), MTime::uiUnit());
00378     mayaegg_cat.info(false)
00379       << "neutral frame " << frame.value() << "\n";
00380     MGlobal::viewFrame(frame);
00381   }
00382 
00383   EggGroup *char_node = new EggGroup(_character_name);
00384   get_egg_data().add_child(char_node);
00385   char_node->set_dart_type(EggGroup::DT_default);
00386 
00387   return convert_hierarchy(char_node);
00388 }
00389 
00390 ////////////////////////////////////////////////////////////////////
00391 //     Function: MayaToEggConverter::convert_char_chan
00392 //       Access: Private
00393 //  Description: Converts the animation as a series of tables to apply
00394 //               to the character model, as retrieved earlier via
00395 //               AC_model.
00396 ////////////////////////////////////////////////////////////////////
00397 bool MayaToEggConverter::
00398 convert_char_chan(double start_frame, double end_frame, double frame_inc,
00399                   double output_frame_rate) {
00400   MStatus status;
00401 
00402   EggTable *root_table_node = new EggTable();
00403   get_egg_data().add_child(root_table_node);
00404   EggTable *bundle_node = new EggTable(_character_name);
00405   bundle_node->set_table_type(EggTable::TT_bundle);
00406   root_table_node->add_child(bundle_node);
00407   EggTable *skeleton_node = new EggTable("<skeleton>");
00408   bundle_node->add_child(skeleton_node);
00409 
00410   // First, walk through the scene graph and build up the table of
00411   // joints.
00412   MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
00413   if (!status) {
00414     status.perror("MItDag constructor");
00415     return false;
00416   }
00417   bool all_ok = true;
00418   while (!dag_iterator.isDone()) {
00419     MDagPath dag_path;
00420     status = dag_iterator.getPath(dag_path);
00421     if (!status) {
00422       status.perror("MItDag::getPath");
00423     } else {
00424       if (!process_chan_node(dag_path, skeleton_node)) {
00425         all_ok = false;
00426       }
00427     }
00428 
00429     dag_iterator.next();
00430   }
00431 
00432   // Now walk through the joints in that table and make sure they're
00433   // all set to use the correct frame frame.
00434   double fps = output_frame_rate / frame_inc;
00435   Tables::iterator ti;
00436   for (ti = _tables.begin(); ti != _tables.end(); ++ti) {
00437     JointAnim *joint_anim = (*ti).second;
00438     nassertr(joint_anim != (JointAnim *)NULL && 
00439              joint_anim->_anim != (EggXfmSAnim *)NULL, false);
00440     joint_anim->_anim->set_fps(fps);
00441   }
00442 
00443   // Now we can get the animation data by walking through all of the
00444   // frames, one at a time, and getting the joint angles at each
00445   // frame.
00446 
00447   // This is just a temporary EggGroup to receive the transform for
00448   // each joint each frame.
00449   PT(EggGroup) tgroup = new EggGroup;
00450 
00451   MTime frame(start_frame, MTime::uiUnit());
00452   MTime frame_stop(end_frame, MTime::uiUnit());
00453   while (frame <= frame_stop) {
00454     if (mayaegg_cat.is_debug()) {
00455       mayaegg_cat.debug(false)
00456         << "frame " << frame.value() << "\n";
00457     } else {
00458       mayaegg_cat.info(false)
00459         << ".";
00460     }
00461     MGlobal::viewFrame(frame);
00462 
00463     for (ti = _tables.begin(); ti != _tables.end(); ++ti) {
00464       JointAnim *joint_anim = (*ti).second;
00465       get_transform(joint_anim->_dag_path, tgroup);
00466       joint_anim->_anim->add_data(tgroup->get_transform());
00467     }
00468 
00469     frame += frame_inc;
00470   }
00471   mayaegg_cat.info(false)
00472     << "\n";
00473 
00474   // Finally, clean up by deleting all of the JointAnim structures we
00475   // created.
00476   for (ti = _tables.begin(); ti != _tables.end(); ++ti) {
00477     JointAnim *joint_anim = (*ti).second;
00478     delete joint_anim;
00479   }
00480   _tables.clear();
00481 
00482   return all_ok;
00483 }
00484 
00485 ////////////////////////////////////////////////////////////////////
00486 //     Function: MayaToEggConverter::convert_hierarchy
00487 //       Access: Private
00488 //  Description: Walks the entire Maya hierarchy, converting it to a
00489 //               corresponding egg hierarchy under the indicated root
00490 //               node.
00491 ////////////////////////////////////////////////////////////////////
00492 bool MayaToEggConverter::
00493 convert_hierarchy(EggGroupNode *egg_root) {
00494   MStatus status;
00495 
00496   MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
00497   if (!status) {
00498     status.perror("MItDag constructor");
00499     return false;
00500   }
00501 
00502   if (_from_selection) {
00503     // Get only the selected geometry.
00504     MSelectionList selection;
00505     status = MGlobal::getActiveSelectionList(selection);
00506     if (!status) {
00507       status.perror("MGlobal::getActiveSelectionList");
00508       return false;
00509     }
00510 
00511     // Get the selected geometry only if the selection is nonempty;
00512     // otherwise, get the whole scene anyway.
00513     if (!selection.isEmpty()) {
00514       bool all_ok = true;
00515       unsigned int length = selection.length();
00516       for (unsigned int i = 0; i < length; i++) {
00517         MDagPath root_path;
00518         status = selection.getDagPath(i, root_path);
00519         if (!status) {
00520           status.perror("MSelectionList::getDagPath");
00521         } else {
00522           // Now traverse through the selected dag path and all nested
00523           // dag paths.
00524           dag_iterator.reset(root_path);
00525           while (!dag_iterator.isDone()) {
00526             MDagPath dag_path;
00527             status = dag_iterator.getPath(dag_path);
00528             if (!status) {
00529               status.perror("MItDag::getPath");
00530             } else {
00531               if (!process_model_node(dag_path, egg_root)) {
00532                 all_ok = false;
00533               }
00534             }
00535             
00536             dag_iterator.next();
00537           }
00538         }
00539       }
00540       return all_ok;
00541 
00542     } else {
00543       mayaegg_cat.info()
00544         << "Selection list is empty.\n";
00545       // Fall through.
00546     }
00547   }
00548 
00549   // Get the entire Maya scene.
00550     
00551   // This while loop walks through the entire Maya hierarchy, one
00552   // node at a time.  Maya's MItDag object automatically performs a
00553   // depth-first traversal of its scene graph.
00554   
00555   bool all_ok = true;
00556   while (!dag_iterator.isDone()) {
00557     MDagPath dag_path;
00558     status = dag_iterator.getPath(dag_path);
00559     if (!status) {
00560       status.perror("MItDag::getPath");
00561     } else {
00562       if (!process_model_node(dag_path, egg_root)) {
00563         all_ok = false;
00564       }
00565     }
00566     
00567     dag_iterator.next();
00568   }
00569   
00570   return all_ok;
00571 }
00572 
00573 ////////////////////////////////////////////////////////////////////
00574 //     Function: MayaToEggConverter::process_model_node
00575 //       Access: Private
00576 //  Description: Converts the indicated Maya node (given a MDagPath,
00577 //               similar in concept to Panda's NodePath) to the
00578 //               corresponding Egg structure.  Returns true if
00579 //               successful, false if an error was encountered.
00580 ////////////////////////////////////////////////////////////////////
00581 bool MayaToEggConverter::
00582 process_model_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
00583   MStatus status;
00584   MFnDagNode dag_node(dag_path, &status);
00585   if (!status) {
00586     status.perror("MFnDagNode constructor");
00587     return false;
00588   }
00589 
00590   if (mayaegg_cat.is_debug()) {
00591     mayaegg_cat.debug()
00592       << dag_path.fullPathName().asChar() << ": " << dag_node.typeName();
00593 
00594     if (MAnimUtil::isAnimated(dag_path)) {
00595       mayaegg_cat.debug(false)
00596         << " (animated)";
00597     }
00598 
00599     mayaegg_cat.debug(false) << "\n";
00600   }
00601 
00602   if (dag_node.inUnderWorld()) {
00603     if (mayaegg_cat.is_debug()) {
00604       mayaegg_cat.debug()
00605         << "Ignoring underworld node " << dag_path.fullPathName().asChar()
00606         << "\n";
00607     }
00608 
00609   } else if (dag_node.isIntermediateObject()) {
00610     if (mayaegg_cat.is_debug()) {
00611       mayaegg_cat.debug()
00612         << "Ignoring intermediate object " << dag_path.fullPathName().asChar()
00613         << "\n";
00614     }
00615 
00616   } else if (dag_path.hasFn(MFn::kCamera)) {
00617     if (mayaegg_cat.is_debug()) {
00618       mayaegg_cat.debug()
00619         << "Ignoring camera node " << dag_path.fullPathName().asChar()
00620         << "\n";
00621     }
00622 
00623   } else if (dag_path.hasFn(MFn::kLight)) {
00624     if (mayaegg_cat.is_debug()) {
00625       mayaegg_cat.debug()
00626         << "Ignoring light node " << dag_path.fullPathName().asChar()
00627         << "\n";
00628     }
00629 
00630   } else if (dag_path.hasFn(MFn::kJoint)) {
00631     // A joint.
00632 
00633     // Don't bother with joints unless we're getting an animatable
00634     // model.
00635     if (_animation_convert == AC_model) {
00636       EggGroup *egg_group = get_egg_group(dag_path, egg_root);
00637 
00638       if (egg_group != (EggGroup *)NULL) {
00639         egg_group->set_group_type(EggGroup::GT_joint);
00640         get_transform(dag_path, egg_group);
00641       }
00642     }
00643 
00644   } else if (dag_path.hasFn(MFn::kNurbsSurface)) {
00645     EggGroup *egg_group = get_egg_group(dag_path, egg_root);
00646 
00647     if (egg_group == (EggGroup *)NULL) {
00648       mayaegg_cat.error()
00649         << "Cannot determine group node.\n";
00650       return false;
00651 
00652     } else {
00653       if (_animation_convert != AC_model) {
00654         get_transform(dag_path, egg_group);
00655       }
00656 
00657       MFnNurbsSurface surface(dag_path, &status);
00658       if (!status) {
00659         mayaegg_cat.info()
00660           << "Error in node " << dag_path.fullPathName().asChar()
00661           << ":\n"
00662           << "  it appears to have a NURBS surface, but does not.\n";
00663       } else {
00664         make_nurbs_surface(dag_path, surface, egg_group, egg_root);
00665       }
00666     }
00667 
00668   } else if (dag_path.hasFn(MFn::kNurbsCurve)) {
00669     // Only convert NurbsCurves if we aren't making an animated model.
00670     // Animated models, as a general rule, don't want these sorts of
00671     // things in them.
00672     if (_animation_convert != AC_model) {
00673       EggGroup *egg_group = get_egg_group(dag_path, egg_root);
00674       
00675       if (egg_group == (EggGroup *)NULL) {
00676         mayaegg_cat.error()
00677           << "Cannot determine group node.\n";
00678         
00679       } else {
00680         get_transform(dag_path, egg_group);
00681         
00682         MFnNurbsCurve curve(dag_path, &status);
00683         if (!status) {
00684           mayaegg_cat.info()
00685             << "Error in node " << dag_path.fullPathName().asChar() << ":\n"
00686             << "  it appears to have a NURBS curve, but does not.\n";
00687         } else {
00688           make_nurbs_curve(dag_path, curve, egg_group, egg_root);
00689         }
00690       }
00691     }
00692       
00693   } else if (dag_path.hasFn(MFn::kMesh)) {
00694     EggGroup *egg_group = get_egg_group(dag_path, egg_root);
00695 
00696     if (egg_group == (EggGroup *)NULL) {
00697       mayaegg_cat.error()
00698         << "Cannot determine group node.\n";
00699       return false;
00700 
00701     } else {
00702       if (_animation_convert != AC_model) {
00703         get_transform(dag_path, egg_group);
00704       }
00705 
00706       MFnMesh mesh(dag_path, &status);
00707       if (!status) {
00708         mayaegg_cat.info()
00709           << "Error in node " << dag_path.fullPathName().asChar() << ":\n"
00710           << "  it appears to have a polygon mesh, but does not.\n";
00711       } else {
00712         make_polyset(dag_path, mesh, egg_group, egg_root);
00713       }
00714     }
00715 
00716   } else if (dag_path.hasFn(MFn::kLocator)) {
00717     EggGroup *egg_group = get_egg_group(dag_path, egg_root);
00718 
00719     if (egg_group == (EggGroup *)NULL) {
00720       mayaegg_cat.error()
00721         << "Cannot determine group node.\n";
00722       return false;
00723 
00724     } else {
00725       if (_animation_convert != AC_model) {
00726         get_transform(dag_path, egg_group);
00727       }
00728       make_locator(dag_path, dag_node, egg_group, egg_root);
00729     }
00730 
00731   } else {
00732     // Get the translation/rotation/scale data
00733     EggGroup *egg_group = get_egg_group(dag_path, egg_root);
00734 
00735     if (egg_group != (EggGroup *)NULL) {
00736       if (_animation_convert != AC_model) {
00737         get_transform(dag_path, egg_group);
00738       }
00739     }
00740   }
00741 
00742   return true;
00743 }
00744 
00745 ////////////////////////////////////////////////////////////////////
00746 //     Function: MayaToEggConverter::process_chan_node
00747 //       Access: Private
00748 //  Description: Similar to process_model_node(), but this code path
00749 //               is followed only when we are building a table of
00750 //               animation data (AC_chan).  It just builds up the
00751 //               EggTable hierarchy according to the joint table.
00752 ////////////////////////////////////////////////////////////////////
00753 bool MayaToEggConverter::
00754 process_chan_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
00755   MStatus status;
00756   MFnDagNode dag_node(dag_path, &status);
00757   if (!status) {
00758     status.perror("MFnDagNode constructor");
00759     return false;
00760   }
00761 
00762   if (dag_path.hasFn(MFn::kJoint)) {
00763     // A joint.
00764 
00765     if (mayaegg_cat.is_debug()) {
00766       mayaegg_cat.debug()
00767         << dag_path.fullPathName().asChar() << ": " << dag_node.typeName();
00768       
00769       if (MAnimUtil::isAnimated(dag_path)) {
00770         mayaegg_cat.debug(false)
00771           << " (animated)";
00772       }
00773       
00774       mayaegg_cat.debug(false) << "\n";
00775     }
00776 
00777     get_egg_table(dag_path, egg_root);
00778   }
00779 
00780   return true;
00781 }
00782 
00783 ////////////////////////////////////////////////////////////////////
00784 //     Function: MayaToEggConverter::get_transform
00785 //       Access: Private
00786 //  Description: Extracts the transform on the indicated Maya node,
00787 //               and applies it to the corresponding Egg node.
00788 ////////////////////////////////////////////////////////////////////
00789 void MayaToEggConverter::
00790 get_transform(const MDagPath &dag_path, EggGroup *egg_group) {
00791   MStatus status;
00792   MObject transformNode = dag_path.transform(&status);
00793   if (!status && status.statusCode() == MStatus::kInvalidParameter) {
00794     // This node has no transform - i.e., it's the world node
00795     return;
00796   }
00797 
00798   // A special case: if the group is a billboard, we center the
00799   // transform on the rotate pivot and ignore whatever transform might
00800   // be there.
00801   if (egg_group->get_billboard_type() != EggGroup::BT_none) {
00802     MFnTransform transform(transformNode, &status);
00803     if (!status) {
00804       status.perror("MFnTransform constructor");
00805       return;
00806     }
00807 
00808     MPoint pivot = transform.rotatePivot(MSpace::kObject, &status);
00809     if (!status) {
00810       status.perror("Can't get rotate pivot");
00811       return;
00812     }
00813 
00814     // We need to convert the pivot to world coordinates.
00815     // Unfortunately, Maya can only tell it to us in local
00816     // coordinates.
00817     MMatrix mat = dag_path.inclusiveMatrix(&status);
00818     if (!status) {
00819       status.perror("Can't get coordinate space for pivot");
00820       return;
00821     }
00822     LMatrix4d n2w(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
00823                   mat[1][0], mat[1][1], mat[1][2], mat[1][3],
00824                   mat[2][0], mat[2][1], mat[2][2], mat[2][3],
00825                   mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
00826     LPoint3d p3d(pivot[0], pivot[1], pivot[2]);
00827     p3d = p3d * n2w;
00828 
00829     if (egg_group->get_parent() != (EggGroupNode *)NULL) {
00830       // Now convert the pivot point into the group's parent's space.
00831       p3d = p3d * egg_group->get_parent()->get_vertex_frame_inv();
00832     }
00833 
00834     egg_group->clear_transform();
00835     egg_group->add_translate(p3d);
00836     return;
00837   }
00838 
00839   switch (_transform_type) {
00840   case TT_all:
00841     break;
00842 
00843   case TT_model:
00844     if (!egg_group->get_model_flag() &&
00845         egg_group->get_dcs_type() == EggGroup::DC_none) {
00846       return;
00847     }
00848     break;
00849 
00850   case TT_dcs: 
00851     if (egg_group->get_dcs_type() == EggGroup::DC_none) {
00852       return;
00853     }
00854     break;
00855 
00856   case TT_none:
00857   case TT_invalid:
00858     return;
00859   }
00860 
00861   // Extract the matrix from the dag path, and convert it to the local
00862   // frame.
00863   MMatrix mat = dag_path.inclusiveMatrix(&status);
00864   if (!status) {
00865     status.perror("Can't get transform matrix");
00866     return;
00867   }
00868   LMatrix4d m4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
00869                 mat[1][0], mat[1][1], mat[1][2], mat[1][3],
00870                 mat[2][0], mat[2][1], mat[2][2], mat[2][3],
00871                 mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
00872   m4d = m4d * egg_group->get_node_frame_inv();
00873   if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
00874     egg_group->add_matrix(m4d);
00875   }
00876 }
00877 
00878 ////////////////////////////////////////////////////////////////////
00879 //     Function: MayaToEggConverter::make_nurbs_surface
00880 //       Access: Private
00881 //  Description: Converts the indicated Maya NURBS surface to a
00882 //               corresponding egg structure, and attaches it to the
00883 //               indicated egg group.
00884 ////////////////////////////////////////////////////////////////////
00885 void MayaToEggConverter::
00886 make_nurbs_surface(const MDagPath &dag_path, MFnNurbsSurface &surface,
00887                    EggGroup *egg_group, EggGroupNode *egg_root) {
00888   MStatus status;
00889   string name = surface.name().asChar();
00890 
00891   if (mayaegg_cat.is_spam()) {
00892     mayaegg_cat.spam()
00893       << "  numCVs: "
00894       << surface.numCVsInU()
00895       << " * "
00896       << surface.numCVsInV()
00897       << "\n";
00898     mayaegg_cat.spam()
00899       << "  numKnots: "
00900       << surface.numKnotsInU()
00901       << " * "
00902       << surface.numKnotsInV()
00903       << "\n";
00904     mayaegg_cat.spam()
00905       << "  numSpans: "
00906       << surface.numSpansInU()
00907       << " * "
00908       << surface.numSpansInV()
00909       << "\n";
00910   }
00911 
00912   MayaShader *shader = _shaders.find_shader_for_node(surface.object());
00913 
00914   if (_polygon_output) {
00915     // If we want polygon output only, tesselate the NURBS and output
00916     // that.
00917     MTesselationParams params;
00918     params.setFormatType(MTesselationParams::kStandardFitFormat);
00919     params.setOutputType(MTesselationParams::kQuads);
00920     params.setStdFractionalTolerance(_polygon_tolerance);
00921 
00922     // We'll create the tesselation as a sibling of the NURBS surface.
00923     // That way we inherit all of the transformations.
00924     MDagPath polyset_path = dag_path;
00925     MObject polyset_parent = polyset_path.node();
00926     MObject polyset =
00927       surface.tesselate(params, polyset_parent, &status);
00928     if (!status) {
00929       status.perror("MFnNurbsSurface::tesselate");
00930       return;
00931     }
00932 
00933     status = polyset_path.push(polyset);
00934     if (!status) {
00935       status.perror("MDagPath::push");
00936     }
00937 
00938     MFnMesh polyset_fn(polyset, &status);
00939     if (!status) {
00940       status.perror("MFnMesh constructor");
00941       return;
00942     }
00943     make_polyset(polyset_path, polyset_fn, egg_group, egg_root, shader);
00944 
00945     // Now remove the polyset we created.
00946     MFnDagNode parent_node(polyset_parent, &status);
00947     if (!status) {
00948       status.perror("MFnDagNode constructor");
00949       return;
00950     }
00951     status = parent_node.removeChild(polyset);
00952     if (!status) {
00953       status.perror("MFnDagNode::removeChild");
00954     }
00955 
00956     return;
00957   }
00958 
00959   MPointArray cv_array;
00960   status = surface.getCVs(cv_array, MSpace::kWorld);
00961   if (!status) {
00962     status.perror("MFnNurbsSurface::getCVs");
00963     return;
00964   }
00965   MDoubleArray u_knot_array, v_knot_array;
00966   status = surface.getKnotsInU(u_knot_array);
00967   if (!status) {
00968     status.perror("MFnNurbsSurface::getKnotsInU");
00969     return;
00970   }
00971   status = surface.getKnotsInV(v_knot_array);
00972   if (!status) {
00973     status.perror("MFnNurbsSurface::getKnotsInV");
00974     return;
00975   }
00976 
00977   /*
00978     We don't use these variables currently.
00979   MFnNurbsSurface::Form u_form = surface.formInU();
00980   MFnNurbsSurface::Form v_form = surface.formInV();
00981   */
00982 
00983   int u_degree = surface.degreeU();
00984   int v_degree = surface.degreeV();
00985 
00986   int u_cvs = surface.numCVsInU();
00987   int v_cvs = surface.numCVsInV();
00988 
00989   int u_knots = surface.numKnotsInU();
00990   int v_knots = surface.numKnotsInV();
00991 
00992   assert(u_knots == u_cvs + u_degree - 1);
00993   assert(v_knots == v_cvs + v_degree - 1);
00994 
00995   string vpool_name = name + ".cvs";
00996   EggVertexPool *vpool = new EggVertexPool(vpool_name);
00997   egg_group->add_child(vpool);
00998 
00999   EggNurbsSurface *egg_nurbs = new EggNurbsSurface(name);
01000   egg_nurbs->setup(u_degree + 1, v_degree + 1,
01001                    u_knots + 2, v_knots + 2);
01002 
01003   int i;
01004 
01005   egg_nurbs->set_u_knot(0, u_knot_array[0]);
01006   for (i = 0; i < u_knots; i++) {
01007     egg_nurbs->set_u_knot(i + 1, u_knot_array[i]);
01008   }
01009   egg_nurbs->set_u_knot(u_knots + 1, u_knot_array[u_knots - 1]);
01010 
01011   egg_nurbs->set_v_knot(0, v_knot_array[0]);
01012   for (i = 0; i < v_knots; i++) {
01013     egg_nurbs->set_v_knot(i + 1, v_knot_array[i]);
01014   }
01015   egg_nurbs->set_v_knot(v_knots + 1, v_knot_array[v_knots - 1]);
01016 
01017   LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
01018 
01019   for (i = 0; i < egg_nurbs->get_num_cvs(); i++) {
01020     int ui = egg_nurbs->get_u_index(i);
01021     int vi = egg_nurbs->get_v_index(i);
01022 
01023     double v[4];
01024     MStatus status = cv_array[v_cvs * ui + vi].get(v);
01025     if (!status) {
01026       status.perror("MPoint::get");
01027     } else {
01028       EggVertex vert;
01029       LPoint4d p4d(v[0], v[1], v[2], v[3]);
01030       p4d = p4d * vertex_frame_inv;
01031       vert.set_pos(p4d);
01032       egg_nurbs->add_vertex(vpool->create_unique_vertex(vert));
01033     }
01034   }
01035 
01036   // Now consider the trim curves, if any.
01037   unsigned num_trims = surface.numRegions();
01038   int trim_curve_index = 0;
01039   for (unsigned ti = 0; ti < num_trims; ti++) {
01040     unsigned num_loops = surface.numBoundaries(ti);
01041 
01042     if (num_loops > 0) {
01043       egg_nurbs->_trims.push_back(EggNurbsSurface::Trim());
01044       EggNurbsSurface::Trim &egg_trim = egg_nurbs->_trims.back();
01045 
01046       for (unsigned li = 0; li < num_loops; li++) {
01047         egg_trim.push_back(EggNurbsSurface::Loop());
01048         EggNurbsSurface::Loop &egg_loop = egg_trim.back();
01049         
01050         MFnNurbsSurface::BoundaryType type =
01051           surface.boundaryType(ti, li, &status);
01052         bool keep_loop = false;
01053         
01054         if (!status) {
01055           status.perror("MFnNurbsSurface::BoundaryType");
01056         } else {
01057           keep_loop = (type == MFnNurbsSurface::kInner ||
01058                        type == MFnNurbsSurface::kOuter);
01059         }
01060         
01061         if (keep_loop) {
01062           unsigned num_edges = surface.numEdges(ti, li);
01063           for (unsigned ei = 0; ei < num_edges; ei++) {
01064             MObjectArray edge = surface.edge(ti, li, ei, true, &status);
01065             if (!status) {
01066               status.perror("MFnNurbsSurface::edge");
01067             } else {
01068               unsigned num_segs = edge.length();
01069               for (unsigned si = 0; si < num_segs; si++) {
01070                 MObject segment = edge[si];
01071                 if (segment.hasFn(MFn::kNurbsCurve)) {
01072                   MFnNurbsCurve curve(segment, &status);
01073                   if (!status) {
01074                     mayaegg_cat.error()
01075                       << "Trim curve appears to be a nurbs curve, but isn't.\n";
01076                   } else {
01077                     // Finally, we have a valid curve!
01078                     EggNurbsCurve *egg_curve =
01079                       make_trim_curve(curve, name, egg_group, trim_curve_index);
01080                     trim_curve_index++;
01081                     if (egg_curve != (EggNurbsCurve *)NULL) {
01082                       egg_loop.push_back(egg_curve);
01083                     }
01084                   }
01085                 } else {
01086                   mayaegg_cat.error()
01087                     << "Trim curve segment is not a nurbs curve.\n";
01088                 }
01089               }
01090             }
01091           }
01092         }
01093       }
01094     }
01095   }
01096 
01097   // We add the NURBS to the group down here, after all of the vpools
01098   // for the trim curves have been added.
01099   egg_group->add_child(egg_nurbs);
01100 
01101   if (shader != (MayaShader *)NULL) {
01102     set_shader_attributes(*egg_nurbs, *shader);
01103   }
01104 }
01105 
01106 ////////////////////////////////////////////////////////////////////
01107 //     Function: MayaToEggConverter::make_trim_curve
01108 //       Access: Private
01109 //  Description: Converts the indicated Maya NURBS trim curve to a
01110 //               corresponding egg structure, and returns it, or NULL
01111 //               if there is a problem.
01112 ////////////////////////////////////////////////////////////////////
01113 EggNurbsCurve *MayaToEggConverter::
01114 make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name,
01115                 EggGroupNode *egg_group, int trim_curve_index) {
01116   if (mayaegg_cat.is_spam()) {
01117     mayaegg_cat.spam()
01118       << "Trim curve:\n";
01119     mayaegg_cat.spam()
01120       << "  numCVs: "
01121       << curve.numCVs()
01122       << "\n";
01123     mayaegg_cat.spam()
01124       << "  numKnots: "
01125       << curve.numKnots()
01126       << "\n";
01127     mayaegg_cat.spam()
01128       << "  numSpans: "
01129       << curve.numSpans()
01130       << "\n";
01131   }
01132 
01133   MStatus status;
01134 
01135   MPointArray cv_array;
01136   status = curve.getCVs(cv_array, MSpace::kWorld);
01137   if (!status) {
01138     status.perror("MFnNurbsCurve::getCVs");
01139     return (EggNurbsCurve *)NULL;
01140   }
01141   MDoubleArray knot_array;
01142   status = curve.getKnots(knot_array);
01143   if (!status) {
01144     status.perror("MFnNurbsCurve::getKnots");
01145     return (EggNurbsCurve *)NULL;
01146   }
01147 
01148   /*
01149   MFnNurbsCurve::Form form = curve.form();
01150   */
01151 
01152   int degree = curve.degree();
01153   int cvs = curve.numCVs();
01154   int knots = curve.numKnots();
01155 
01156   assert(knots == cvs + degree - 1);
01157 
01158   string trim_name = "trim" + format_string(trim_curve_index);
01159 
01160   string vpool_name = nurbs_name + "." + trim_name;
01161   EggVertexPool *vpool = new EggVertexPool(vpool_name);
01162   egg_group->add_child(vpool);
01163 
01164   EggNurbsCurve *egg_curve = new EggNurbsCurve(trim_name);
01165   egg_curve->setup(degree + 1, knots + 2);
01166 
01167   int i;
01168 
01169   egg_curve->set_knot(0, knot_array[0]);
01170   for (i = 0; i < knots; i++) {
01171     egg_curve->set_knot(i + 1, knot_array[i]);
01172   }
01173   egg_curve->set_knot(knots + 1, knot_array[knots - 1]);
01174 
01175   for (i = 0; i < egg_curve->get_num_cvs(); i++) {
01176     double v[4];
01177     MStatus status = cv_array[i].get(v);
01178     if (!status) {
01179       status.perror("MPoint::get");
01180     } else {
01181       EggVertex vert;
01182       vert.set_pos(LPoint3d(v[0], v[1], v[3]));
01183       egg_curve->add_vertex(vpool->create_unique_vertex(vert));
01184     }
01185   }
01186 
01187   return egg_curve;
01188 }
01189 
01190 ////////////////////////////////////////////////////////////////////
01191 //     Function: MayaToEggConverter::make_nurbs_curve
01192 //       Access: Private
01193 //  Description: Converts the indicated Maya NURBS curve (a standalone
01194 //               curve, not a trim curve) to a corresponding egg
01195 //               structure and attaches it to the indicated egg group.
01196 ////////////////////////////////////////////////////////////////////
01197 void MayaToEggConverter::
01198 make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
01199                  EggGroup *egg_group, EggGroupNode *) {
01200   MStatus status;
01201   string name = curve.name().asChar();
01202 
01203   if (mayaegg_cat.is_spam()) {
01204     mayaegg_cat.spam()
01205       << "  numCVs: "
01206       << curve.numCVs()
01207       << "\n";
01208     mayaegg_cat.spam()
01209       << "  numKnots: "
01210       << curve.numKnots()
01211       << "\n";
01212     mayaegg_cat.spam()
01213       << "  numSpans: "
01214       << curve.numSpans()
01215       << "\n";
01216   }
01217 
01218   MPointArray cv_array;
01219   status = curve.getCVs(cv_array, MSpace::kWorld);
01220   if (!status) {
01221     status.perror("MFnNurbsCurve::getCVs");
01222     return;
01223   }
01224   MDoubleArray knot_array;
01225   status = curve.getKnots(knot_array);
01226   if (!status) {
01227     status.perror("MFnNurbsCurve::getKnots");
01228     return;
01229   }
01230 
01231   /*
01232   MFnNurbsCurve::Form form = curve.form();
01233   */
01234 
01235   int degree = curve.degree();
01236   int cvs = curve.numCVs();
01237   int knots = curve.numKnots();
01238 
01239   assert(knots == cvs + degree - 1);
01240 
01241   string vpool_name = name + ".cvs";
01242   EggVertexPool *vpool = new EggVertexPool(vpool_name);
01243   egg_group->add_child(vpool);
01244 
01245   EggNurbsCurve *egg_curve = new EggNurbsCurve(name);
01246   egg_group->add_child(egg_curve);
01247   egg_curve->setup(degree + 1, knots + 2);
01248 
01249   int i;
01250 
01251   egg_curve->set_knot(0, knot_array[0]);
01252   for (i = 0; i < knots; i++) {
01253     egg_curve->set_knot(i + 1, knot_array[i]);
01254   }
01255   egg_curve->set_knot(knots + 1, knot_array[knots - 1]);
01256 
01257   LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
01258 
01259   for (i = 0; i < egg_curve->get_num_cvs(); i++) {
01260     double v[4];
01261     MStatus status = cv_array[i].get(v);
01262     if (!status) {
01263       status.perror("MPoint::get");
01264     } else {
01265       EggVertex vert;
01266       LPoint4d p4d(v[0], v[1], v[2], v[3]);
01267       p4d = p4d * vertex_frame_inv;
01268       vert.set_pos(p4d);
01269       egg_curve->add_vertex(vpool->create_unique_vertex(vert));
01270     }
01271   }
01272 
01273   MayaShader *shader = _shaders.find_shader_for_node(curve.object());
01274   if (shader != (MayaShader *)NULL) {
01275     set_shader_attributes(*egg_curve, *shader);
01276   }
01277 }
01278 
01279 ////////////////////////////////////////////////////////////////////
01280 //     Function: MayaToEggConverter::make_polyset
01281 //       Access: Private
01282 //  Description: Converts the indicated Maya polyset to a bunch of
01283 //               EggPolygons and parents them to the indicated egg
01284 //               group.
01285 ////////////////////////////////////////////////////////////////////
01286 void MayaToEggConverter::
01287 make_polyset(const MDagPath &dag_path, const MFnMesh &mesh,
01288              EggGroup *egg_group, EggGroupNode *egg_root,
01289              MayaShader *default_shader) {
01290   MStatus status;
01291   string name = mesh.name().asChar();
01292 
01293   MObject mesh_object = mesh.object();
01294   bool double_sided = false;
01295   get_bool_attribute(mesh_object, "doubleSided", double_sided);
01296 
01297   if (mayaegg_cat.is_spam()) {
01298     mayaegg_cat.spam()
01299       << "  numPolygons: "
01300       << mesh.numPolygons()
01301       << "\n";
01302     mayaegg_cat.spam()
01303       << "  numVertices: "
01304       << mesh.numVertices()
01305       << "\n";
01306   }
01307 
01308   if (mesh.numPolygons() == 0) {
01309     if (mayaegg_cat.is_debug()) {
01310       mayaegg_cat.debug()
01311         << "Ignoring empty mesh " << name << "\n";
01312     }
01313     return;
01314   }
01315 
01316   string vpool_name = name + ".verts";
01317   EggVertexPool *vpool = new EggVertexPool(vpool_name);
01318   egg_group->add_child(vpool);
01319 
01320   // One way to convert the mesh would be to first get out all the
01321   // vertices in the mesh and add them into the vpool, then when we
01322   // traverse the polygons we would only have to index them into the
01323   // vpool according to their Maya vertex index.
01324 
01325   // Unfortunately, since Maya may store multiple normals and/or
01326   // colors for each vertex according to which polygon it is in, that
01327   // approach won't necessarily work.  In egg, those split-property
01328   // vertices have to become separate vertices.  So instead of adding
01329   // all the vertices up front, we'll start with an empty vpool, and
01330   // add vertices to it on the fly.
01331 
01332   MObject component_obj;
01333   MItMeshPolygon pi(dag_path, component_obj, &status);
01334   if (!status) {
01335     status.perror("MItMeshPolygon constructor");
01336     return;
01337   }
01338 
01339   MObjectArray shaders;
01340   MIntArray poly_shader_indices;
01341 
01342   status = mesh.getConnectedShaders(dag_path.instanceNumber(),
01343                                     shaders, poly_shader_indices);
01344   if (!status) {
01345     status.perror("MFnMesh::getConnectedShaders");
01346   }
01347 
01348   // We will need to transform all vertices from world coordinate
01349   // space into the vertex space appropriate to this node.  Usually,
01350   // this is the same thing as world coordinate space, and this matrix
01351   // will be identity; but if the node is under an instance
01352   // (particularly, for instance, a billboard) then the vertex space
01353   // will be different from world space.
01354   LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
01355 
01356   while (!pi.isDone()) {
01357     EggPolygon *egg_poly = new EggPolygon;
01358     egg_group->add_child(egg_poly);
01359 
01360     egg_poly->set_bface_flag(double_sided);
01361 
01362     // Determine the shader for this particular polygon.
01363     MayaShader *shader = NULL;
01364     int index = pi.index();
01365     nassertv(index >= 0 && index < (int)poly_shader_indices.length());
01366     int shader_index = poly_shader_indices[index];
01367     if (shader_index != -1) {
01368       nassertv(shader_index >= 0 && shader_index < (int)shaders.length());
01369       MObject engine = shaders[shader_index];
01370       shader =
01371         _shaders.find_shader_for_shading_engine(engine);
01372 
01373     } else if (default_shader != (MayaShader *)NULL) {
01374       shader = default_shader;
01375     }
01376 
01377     const MayaShaderColorDef &color_def = shader->_color;
01378 
01379     // Since a texture completely replaces a polygon or vertex color,
01380     // we need to know up front whether we have a texture.
01381     bool has_texture = false;
01382     if (shader != (MayaShader *)NULL) {
01383       has_texture = color_def._has_texture;
01384     }
01385 
01386     // Get the vertices for the polygon.
01387     long num_verts = pi.polygonVertexCount();
01388     long i;
01389     LPoint3d centroid(0.0, 0.0, 0.0);
01390 
01391     if (shader != (MayaShader *)NULL && color_def.has_projection()) {
01392       // If the shader has a projection, we may need to compute the
01393       // polygon's centroid to avoid seams at the edges.
01394       for (i = 0; i < num_verts; i++) {
01395         MPoint p = pi.point(i, MSpace::kWorld);
01396         LPoint3d p3d(p[0], p[1], p[2]);
01397         p3d = p3d * vertex_frame_inv;
01398         centroid += p3d;
01399       }
01400       centroid /= (double)num_verts;
01401     }
01402 
01403     for (i = 0; i < num_verts; i++) {
01404       EggVertex vert;
01405 
01406       MPoint p = pi.point(i, MSpace::kWorld);
01407       LPoint3d p3d(p[0], p[1], p[2]);
01408       p3d = p3d * vertex_frame_inv;
01409       vert.set_pos(p3d);
01410 
01411       MVector n;
01412       status = pi.getNormal(i, n, MSpace::kWorld);
01413       if (!status) {
01414         status.perror("MItMeshPolygon::getNormal");
01415       } else {
01416         LVector3d n3d(n[0], n[1], n[2]);
01417         n3d = n3d * vertex_frame_inv;
01418         vert.set_normal(n3d);
01419       }
01420 
01421       if (shader != (MayaShader *)NULL && color_def.has_projection()) {
01422         // If the shader has a projection, use it instead of the
01423         // polygon's built-in UV's.
01424         vert.set_uv(color_def.project_uv(p3d, centroid));
01425 
01426       } else if (pi.hasUVs()) {
01427         // Get the UV's from the polygon.
01428         float2 uvs;
01429         status = pi.getUV(i, uvs);
01430         if (!status) {
01431           status.perror("MItMeshPolygon::getUV");
01432         } else {
01433           vert.set_uv(TexCoordd(uvs[0], uvs[1]));
01434         }
01435       }
01436 
01437       if (pi.hasColor() && !has_texture) {
01438         MColor c;
01439         status = pi.getColor(c, i);
01440         if (!status) {
01441           status.perror("MItMeshPolygon::getColor");
01442         } else {
01443           vert.set_color(Colorf(c.r, c.g, c.b, 1.0));
01444         }
01445       }
01446 
01447       vert.set_external_index(pi.vertexIndex(i, &status));
01448 
01449       egg_poly->add_vertex(vpool->create_unique_vertex(vert));
01450     }
01451 
01452     // Now apply the shader.
01453     if (shader != (MayaShader *)NULL) {
01454       set_shader_attributes(*egg_poly, *shader);
01455     }
01456 
01457     pi.next();
01458   }
01459 
01460   // Now that we've added all the polygons (and created all the
01461   // vertices), go back through the vertex pool and set up the
01462   // appropriate joint membership for each of the vertices.
01463   bool got_weights = false;
01464 
01465   pvector<EggGroup *> joints;
01466   MFloatArray weights;
01467   if (_animation_convert == AC_model) {
01468     got_weights = 
01469       get_vertex_weights(dag_path, mesh, egg_root, joints, weights);
01470   }
01471 
01472   if (got_weights) {
01473     int num_joints = joints.size();
01474     int num_weights = (int)weights.length();
01475     int num_verts = num_weights / num_joints;
01476     // The number of weights should be an even multiple of verts *
01477     // joints.
01478     nassertv(num_weights == num_verts * num_joints);
01479 
01480     EggVertexPool::iterator vi;
01481     for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
01482       EggVertex *vert = (*vi);
01483       int maya_vi = vert->get_external_index();
01484       nassertv(maya_vi >= 0 && maya_vi < num_verts);
01485 
01486       for (int ji = 0; ji < num_joints; ++ji) {
01487         float weight = weights[maya_vi * num_joints + ji];
01488         if (weight != 0.0f) {
01489           EggGroup *joint = joints[ji];
01490           if (joint != (EggGroup *)NULL) {
01491             joint->ref_vertex(vert, weight);
01492           }
01493         }
01494       }
01495     }
01496   }
01497 }
01498 
01499 ////////////////////////////////////////////////////////////////////
01500 //     Function: MayaToEggConverter::make_locator
01501 //       Access: Private
01502 //  Description: Locators are used in Maya to indicate a particular
01503 //               position in space to the user or the modeler.  We
01504 //               represent that in egg with an ordinary Group node,
01505 //               which we transform by the locator's position, so that
01506 //               the indicated point becomes the origin at this node
01507 //               and below.
01508 ////////////////////////////////////////////////////////////////////
01509 void MayaToEggConverter::
01510 make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
01511              EggGroup *egg_group, EggGroupNode *egg_root) {
01512   MStatus status;
01513 
01514   unsigned int num_children = dag_node.childCount();
01515   MObject locator;
01516   bool found_locator = false;
01517   for (unsigned int ci = 0; ci < num_children && !found_locator; ci++) {
01518     locator = dag_node.child(ci);
01519     found_locator = (locator.apiType() == MFn::kLocator);
01520   }
01521 
01522   if (!found_locator) {
01523     mayaegg_cat.error()
01524       << "Couldn't find locator within locator node " 
01525       << dag_path.fullPathName().asChar() << "\n";
01526     return;
01527   }
01528 
01529   LPoint3d p3d;
01530   if (!get_vec3d_attribute(locator, "localPosition", p3d)) {
01531     mayaegg_cat.error()
01532       << "Couldn't get position of locator " 
01533       << dag_path.fullPathName().asChar() << "\n";
01534     return;
01535   }
01536 
01537   // We need to convert the position to world coordinates.  For some
01538   // reason, Maya can only tell it to us in local coordinates.
01539   MMatrix mat = dag_path.inclusiveMatrix(&status);
01540   if (!status) {
01541     status.perror("Can't get coordinate space for locator");
01542     return;
01543   }
01544   LMatrix4d n2w(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
01545                 mat[1][0], mat[1][1], mat[1][2], mat[1][3],
01546                 mat[2][0], mat[2][1], mat[2][2], mat[2][3],
01547                 mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
01548   p3d = p3d * n2w;
01549 
01550   // Now convert the locator point into the group's space.
01551   p3d = p3d * egg_group->get_node_frame_inv();
01552 
01553   egg_group->add_translate(p3d);
01554 
01555   // Presumably, the locator's position has some meaning to the
01556   // end-user, so we will implicitly tag it with the DCS flag so it
01557   // won't get flattened out.
01558   egg_group->set_dcs_type(EggGroup::DC_net);
01559 }
01560 
01561 ////////////////////////////////////////////////////////////////////
01562 //     Function: MayaToEggConverter::get_vertex_weights
01563 //       Access: Private
01564 //  Description: 
01565 ////////////////////////////////////////////////////////////////////
01566 bool MayaToEggConverter::
01567 get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
01568                    EggGroupNode *egg_root,
01569                    pvector<EggGroup *> &joints, MFloatArray &weights) {
01570   MStatus status;
01571   
01572   // Since we are working with a mesh the input attribute that 
01573   // creates the mesh is named "inMesh" 
01574   // 
01575   MObject attr = mesh.attribute("inMesh"); 
01576   
01577   // Create the plug to the "inMesh" attribute then use the 
01578   // DG iterator to walk through the DG, at the node level.
01579   // 
01580   MPlug history(mesh.object(), attr); 
01581   MItDependencyGraph it(history, MFn::kDependencyNode, 
01582                         MItDependencyGraph::kUpstream, 
01583                         MItDependencyGraph::kDepthFirst, 
01584                         MItDependencyGraph::kNodeLevel);
01585 
01586   while (!it.isDone()) {
01587     // We will walk along the node level of the DG until we 
01588     // spot a skinCluster node.
01589     // 
01590     MObject c_node = it.thisNode(); 
01591     if (c_node.hasFn(MFn::kSkinClusterFilter)) { 
01592       // We've found the cluster handle. Try to get the weight
01593       // data.
01594       // 
01595       MFnSkinCluster cluster(c_node, &status); 
01596       if (!status) {
01597         status.perror("MFnSkinCluster constructor");
01598         return false;
01599       }
01600 
01601       // Get the set of objects that influence the vertices of this
01602       // mesh.  Hopefully these will all be joints.
01603       MDagPathArray influence_objects;
01604       cluster.influenceObjects(influence_objects, &status); 
01605       if (!status) {
01606         status.perror("MFnSkinCluster::influenceObjects");
01607 
01608       } else {
01609         // Fill up the vector with the corresponding table of egg
01610         // groups for each joint.
01611         joints.clear();
01612         for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
01613           MDagPath joint_dag_path = influence_objects[oi];
01614           EggGroup *joint = get_egg_group(joint_dag_path, egg_root);
01615           joints.push_back(joint);
01616         }
01617 
01618         // Now use a component object to retrieve all of the weight
01619         // data in one API call.
01620         MFnSingleIndexedComponent sic; 
01621         MObject sic_object = sic.create(MFn::kMeshVertComponent); 
01622         sic.setCompleteData(mesh.numVertices()); 
01623         unsigned influence_count; 
01624 
01625         status = cluster.getWeights(dag_path, sic_object, 
01626                                     weights, influence_count); 
01627         if (!status) {
01628           status.perror("MFnSkinCluster::getWeights");
01629         } else {
01630           if (influence_count != influence_objects.length()) {
01631             mayaegg_cat.error()
01632               << "MFnSkinCluster::influenceObjects() returns " 
01633               << influence_objects.length()
01634               << " objects, but MFnSkinCluster::getWeights() reports "
01635               << influence_count << " objects.\n";
01636             
01637           } else {
01638             // We've got the weights and the set of objects.  That's all
01639             // we need.
01640             return true;
01641           }
01642         }
01643       }
01644     }
01645 
01646     it.next();
01647   }
01648   
01649   mayaegg_cat.error()
01650     << "Unable to find a cluster handle for the DG node.\n"; 
01651   return false;
01652 }
01653 
01654 
01655 ////////////////////////////////////////////////////////////////////
01656 //     Function: MayaToEggConverter::get_egg_group
01657 //       Access: Private
01658 //  Description: Returns the EggGroup corresponding to the indicated
01659 //               fully-qualified Maya path name.  If there is not
01660 //               already an EggGroup corresponding to this Maya path,
01661 //               creates one and returns it.
01662 //
01663 //               In this way we generate a unique EggGroup for each
01664 //               Maya node we care about about, and also preserve the
01665 //               Maya hierarchy sensibly.
01666 ////////////////////////////////////////////////////////////////////
01667 EggGroup *MayaToEggConverter::
01668 get_egg_group(const MDagPath &dag_path, EggGroupNode *egg_root) {
01669   return r_get_egg_group(dag_path.fullPathName().asChar(), dag_path, egg_root);
01670 }
01671 
01672 ////////////////////////////////////////////////////////////////////
01673 //     Function: MayaToEggConverter::r_get_egg_group
01674 //       Access: Private
01675 //  Description: The recursive implementation of get_egg_group().
01676 ////////////////////////////////////////////////////////////////////
01677 EggGroup *MayaToEggConverter::
01678 r_get_egg_group(const string &name, const MDagPath &dag_path,
01679                 EggGroupNode *egg_root) {
01680   // If we have already encountered this pathname, return the
01681   // corresponding EggGroup immediately.
01682   Groups::const_iterator gi = _groups.find(name);
01683   if (gi != _groups.end()) {
01684     return (*gi).second;
01685   }
01686 
01687   // Otherwise, we have to create it.  Do this recursively, so we
01688   // create each node along the path.
01689   EggGroup *egg_group;
01690 
01691   if (name.empty()) {
01692     // This is the top.
01693     egg_group = (EggGroup *)NULL;
01694 
01695   } else {
01696     // Maya uses vertical bars to separate path components.  Remove
01697     // everything from the rightmost bar on; this will give us the
01698     // parent's path name.
01699     size_t bar = name.rfind("|");
01700     string parent_name, local_name;
01701     if (bar != string::npos) {
01702       parent_name = name.substr(0, bar);
01703       local_name = name.substr(bar + 1);
01704     } else {
01705       local_name = name;
01706     }
01707 
01708     EggGroup *parent_egg_group = 
01709       r_get_egg_group(parent_name, dag_path, egg_root);
01710     egg_group = new EggGroup(local_name);
01711 
01712     if (parent_egg_group != (EggGroup *)NULL) {
01713       parent_egg_group->add_child(egg_group);
01714     } else {
01715       egg_root->add_child(egg_group);
01716     }
01717 
01718     // Check for an object type setting, from Oliver's plug-in.
01719     MObject dag_object = dag_path.node();
01720     string object_type;
01721     if (get_enum_attribute(dag_object, "eggObjectTypes1", object_type)) {
01722       egg_group->add_object_type(object_type);
01723     }
01724     if (get_enum_attribute(dag_object, "eggObjectTypes2", object_type)) {
01725       egg_group->add_object_type(object_type);
01726     }
01727     if (get_enum_attribute(dag_object, "eggObjectTypes3", object_type)) {
01728       egg_group->add_object_type(object_type);
01729     }
01730 
01731     // We treat the object type "billboard" as a special case: we
01732     // apply this one right away and also flag the group as an
01733     // instance.
01734     if (egg_group->has_object_type("billboard")) {    
01735       egg_group->remove_object_type("billboard");
01736       egg_group->set_group_type(EggGroup::GT_instance);
01737       egg_group->set_billboard_type(EggGroup::BT_axis);
01738 
01739     } else if (egg_group->has_object_type("billboard-point")) {    
01740       egg_group->remove_object_type("billboard-point");
01741       egg_group->set_group_type(EggGroup::GT_instance);
01742       egg_group->set_billboard_type(EggGroup::BT_point_camera_relative);
01743     }
01744 
01745     // We also treat the object type "dcs" and "model" as a special
01746     // case, so we can test for these flags later.
01747     if (egg_group->has_object_type("dcs")) {
01748       egg_group->remove_object_type("dcs");
01749       egg_group->set_dcs_type(EggGroup::DC_default);
01750     }
01751     if (egg_group->has_object_type("model")) {
01752       egg_group->remove_object_type("model");
01753       egg_group->set_model_flag(true);
01754     }
01755   }
01756 
01757   _groups.insert(Groups::value_type(name, egg_group));
01758   return egg_group;
01759 }
01760 
01761 ////////////////////////////////////////////////////////////////////
01762 //     Function: MayaToEggConverter::get_egg_table
01763 //       Access: Private
01764 //  Description: Returns the EggTable corresponding to the indicated
01765 //               fully-qualified Maya path name.  This is similar to
01766 //               get_egg_group(), but this variant is used only when
01767 //               we are building a channel file, which is just a
01768 //               hierarchical collection of animation tables.
01769 ////////////////////////////////////////////////////////////////////
01770 MayaToEggConverter::JointAnim *MayaToEggConverter::
01771 get_egg_table(const MDagPath &dag_path, EggGroupNode *egg_root) {
01772   string name = dag_path.fullPathName().asChar();
01773 
01774   // If we have already encountered this pathname, return the
01775   // corresponding EggTable immediately.
01776   Tables::const_iterator ti = _tables.find(name);
01777   if (ti != _tables.end()) {
01778     return (*ti).second;
01779   }
01780 
01781   // Otherwise, we have to create it.
01782   JointAnim *joint_anim;
01783 
01784   if (name.empty()) {
01785     // This is the top.
01786     joint_anim = (JointAnim *)NULL;
01787 
01788   } else {
01789     // Maya uses vertical bars to separate path components.  Remove
01790     // everything from the rightmost bar on; this will give us the
01791     // parent's path name.
01792     size_t bar = name.rfind("|");
01793     string parent_name, local_name;
01794     if (bar != string::npos) {
01795       parent_name = name.substr(0, bar);
01796       local_name = name.substr(bar + 1);
01797     } else {
01798       local_name = name;
01799     }
01800 
01801     // Look only one level up for the parent.  If it hasn't been
01802     // defined yet, don't define it; instead, just start from the
01803     // root.
01804     JointAnim *parent_joint_anim = NULL;
01805     if (!parent_name.empty()) {
01806       ti = _tables.find(parent_name);
01807       if (ti != _tables.end()) {
01808         parent_joint_anim = (*ti).second;
01809       }
01810     }
01811 
01812     joint_anim = new JointAnim;
01813     joint_anim->_dag_path = dag_path;
01814     joint_anim->_table = new EggTable(local_name);
01815     joint_anim->_anim = new EggXfmSAnim("xform", _egg_data->get_coordinate_system());
01816     joint_anim->_table->add_child(joint_anim->_anim);
01817 
01818     if (parent_joint_anim != (JointAnim *)NULL) {
01819       parent_joint_anim->_table->add_child(joint_anim->_table);
01820     } else {
01821       egg_root->add_child(joint_anim->_table);
01822     }
01823   }
01824 
01825   _tables.insert(Tables::value_type(name, joint_anim));
01826   return joint_anim;
01827 }
01828 
01829 ////////////////////////////////////////////////////////////////////
01830 //     Function: MayaShader::set_shader_attributes
01831 //       Access: Private
01832 //  Description: Applies the known shader attributes to the indicated
01833 //               egg primitive.
01834 ////////////////////////////////////////////////////////////////////
01835 void MayaToEggConverter::
01836 set_shader_attributes(EggPrimitive &primitive, const MayaShader &shader) {
01837   // In Maya, a polygon is either textured or colored.  The texture,
01838   // if present, replaces the color.
01839   const MayaShaderColorDef &color_def = shader._color;
01840   const MayaShaderColorDef &trans_def = shader._transparency;
01841   if (color_def._has_texture || trans_def._has_texture) {
01842     EggTexture tex(shader.get_name(), "");
01843 
01844     if (color_def._has_texture) {
01845       // If we have a texture on color, apply it as the filename.
01846       Filename filename = Filename::from_os_specific(color_def._texture);
01847       Filename fullpath = 
01848         _path_replace->match_path(filename, get_texture_path());
01849       tex.set_filename(_path_replace->store_path(fullpath));
01850       tex.set_fullpath(fullpath);
01851       apply_texture_properties(tex, color_def);
01852 
01853       // If we also have a texture on transparency, apply it as the
01854       // alpha filename.
01855       if (trans_def._has_texture) {
01856         if (color_def._wrap_u != trans_def._wrap_u ||
01857             color_def._wrap_u != trans_def._wrap_u) {
01858           mayaegg_cat.warning()
01859             << "Shader " << shader.get_name()
01860             << " has contradictory wrap modes on color and texture.\n";
01861         }
01862           
01863         if (!compare_texture_properties(tex, trans_def)) {
01864           // Only report each broken shader once.
01865           static pset<string> bad_shaders;
01866           if (bad_shaders.insert(shader.get_name()).second) {
01867             mayaegg_cat.error()
01868               << "Color and transparency texture properties differ on shader "
01869               << shader.get_name() << "\n";
01870           }
01871         }
01872         tex.set_format(EggTexture::F_rgba);
01873           
01874         // We should try to be smarter about whether the transparency
01875         // value is connected to the texture's alpha channel or to its
01876         // grayscale channel.  However, I'm not sure how to detect
01877         // this at the moment; rather than spending days trying to
01878         // figure out, for now I'll just assume that if the same
01879         // texture image is used for both color and transparency, then
01880         // the artist meant to use the alpha channel for transparency.
01881         if (trans_def._texture == color_def._texture) {
01882           // That means that we don't need to do anything special: use
01883           // all the channels of the texture.
01884 
01885         } else {
01886           // Otherwise, pull the alpha channel from the other image
01887           // file.  Ideally, we should figure out which channel from
01888           // the other image supplies alpha (and specify this via
01889           // set_alpha_file_channel()), but for now we assume it comes
01890           // from the grayscale data.
01891           filename = Filename::from_os_specific(trans_def._texture);
01892           fullpath = _path_replace->match_path(filename, get_texture_path());
01893           tex.set_alpha_filename(_path_replace->store_path(fullpath));
01894           tex.set_alpha_fullpath(fullpath);
01895         }
01896 
01897       } else {
01898         // If there is no transparency texture specified, we don't
01899         // have any transparency, so tell the egg format to ignore any
01900         // alpha channel that might be on the color texture.
01901         tex.set_format(EggTexture::F_rgb);
01902       }
01903 
01904     } else {  // trans_def._has_texture
01905       // We have a texture on transparency only.  Apply it as the
01906       // primary filename, and set the format accordingly.
01907       Filename filename = Filename::from_os_specific(trans_def._texture);
01908       Filename fullpath = 
01909         _path_replace->match_path(filename, get_texture_path());
01910       tex.set_filename(_path_replace->store_path(fullpath));
01911       tex.set_fullpath(fullpath);
01912       tex.set_format(EggTexture::F_alpha);
01913       apply_texture_properties(tex, trans_def);
01914     }
01915   
01916     EggTexture *new_tex =
01917       _textures.create_unique_texture(tex, ~EggTexture::E_tref_name);
01918     
01919     primitive.set_texture(new_tex);
01920 
01921   }
01922 
01923   // Also apply an overall color to the primitive.
01924   Colorf rgba = shader.get_rgba();
01925 
01926   // This is a placeholder for a parameter on the shader or group that
01927   // we have yet to define.
01928   static const bool modulate = false;
01929   
01930   if (!modulate) {
01931     // If modulate is not specified, the existence of a texture on
01932     // either color channel completely replaces the flat color.
01933     if (color_def._has_texture) {
01934       rgba[0] = 1.0f;
01935       rgba[1] = 1.0f;
01936       rgba[2] = 1.0f;
01937     }
01938     if (trans_def._has_texture) {
01939       rgba[3] = 1.0f;
01940     }
01941   }
01942 
01943   // But the color gain always gets applied.
01944   rgba[0] *= color_def._color_gain[0];
01945   rgba[1] *= color_def._color_gain[1];
01946   rgba[2] *= color_def._color_gain[2];
01947 
01948   primitive.set_color(rgba);
01949 }
01950 
01951 ////////////////////////////////////////////////////////////////////
01952 //     Function: MayaShader::apply_texture_properties
01953 //       Access: Private
01954 //  Description: Applies all the appropriate texture properties to the
01955 //               EggTexture object, including wrap modes and texture
01956 //               matrix.
01957 ////////////////////////////////////////////////////////////////////
01958 void MayaToEggConverter::
01959 apply_texture_properties(EggTexture &tex, const MayaShaderColorDef &color_def) {
01960   // Let's mipmap all textures by default.
01961   tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
01962   tex.set_magfilter(EggTexture::FT_linear);
01963 
01964   EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
01965   EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
01966 
01967   tex.set_wrap_u(wrap_u);
01968   tex.set_wrap_v(wrap_v);
01969   
01970   LMatrix3d mat = color_def.compute_texture_matrix();
01971   if (!mat.almost_equal(LMatrix3d::ident_mat())) {
01972     tex.set_transform(mat);
01973   }
01974 }
01975 
01976 ////////////////////////////////////////////////////////////////////
01977 //     Function: MayaShader::compare_texture_properties
01978 //       Access: Private
01979 //  Description: Compares the texture properties already on the
01980 //               texture (presumably set by a previous call to
01981 //               apply_texture_properties()) and returns false if they
01982 //               differ from that specified by the indicated color_def
01983 //               object, or true if they match.
01984 ////////////////////////////////////////////////////////////////////
01985 bool MayaToEggConverter::
01986 compare_texture_properties(EggTexture &tex, 
01987                            const MayaShaderColorDef &color_def) {
01988   bool okflag = true;
01989 
01990   EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
01991   EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
01992   
01993   if (wrap_u != tex.determine_wrap_u()) {
01994     // Choose the more general of the two.
01995     if (wrap_u == EggTexture::WM_repeat) {
01996       tex.set_wrap_u(wrap_u);
01997     }
01998     okflag = false;
01999   }
02000   if (wrap_v != tex.determine_wrap_v()) {
02001     if (wrap_v == EggTexture::WM_repeat) {
02002       tex.set_wrap_v(wrap_v);
02003     }
02004     okflag = false;
02005   }
02006   
02007   LMatrix3d mat = color_def.compute_texture_matrix();
02008   if (!mat.almost_equal(tex.get_transform())) {
02009     okflag = false;
02010   }
02011 
02012   return okflag;
02013 }
02014 
02015 ////////////////////////////////////////////////////////////////////
02016 //     Function: MayaShader::reparent_decals
02017 //       Access: Private
02018 //  Description: Recursively walks the egg hierarchy, reparenting
02019 //               "decal" type nodes below their corresponding
02020 //               "decalbase" type nodes, and setting the flags.
02021 //
02022 //               Returns true on success, false if some nodes were
02023 //               incorrect.
02024 ////////////////////////////////////////////////////////////////////
02025 bool MayaToEggConverter::
02026 reparent_decals(EggGroupNode *egg_parent) {
02027   bool okflag = true;
02028 
02029   // First, walk through all children of this node, looking for the
02030   // one decal base, if any.
02031   EggGroup *decal_base = (EggGroup *)NULL;
02032   pvector<EggGroup *> decal_children;
02033 
02034   EggGroupNode::iterator ci;
02035   for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
02036     EggNode *child =  (*ci);
02037     if (child->is_of_type(EggGroup::get_class_type())) {
02038       EggGroup *child_group = DCAST(EggGroup, child);
02039       if (child_group->has_object_type("decalbase")) {
02040         if (decal_base != (EggNode *)NULL) {
02041           mayaegg_cat.error()
02042             << "Two children of " << egg_parent->get_name()
02043             << " both have decalbase set: " << decal_base->get_name()
02044             << " and " << child_group->get_name() << "\n";
02045           okflag = false;
02046         }
02047         child_group->remove_object_type("decalbase");
02048         decal_base = child_group;
02049 
02050       } else if (child_group->has_object_type("decal")) {
02051         child_group->remove_object_type("decal");
02052         decal_children.push_back(child_group);
02053       }
02054     }
02055   }
02056 
02057   if (decal_base == (EggGroup *)NULL) {
02058     if (!decal_children.empty()) {
02059       mayaegg_cat.warning()
02060         << decal_children.front()->get_name()
02061         << " has decal, but no sibling node has decalbase.\n";
02062     }
02063 
02064   } else {
02065     if (decal_children.empty()) {
02066       mayaegg_cat.warning()
02067         << decal_base->get_name()
02068         << " has decalbase, but no sibling nodes have decal.\n";
02069 
02070     } else {
02071       // All the decal children get moved to be a child of decal base.
02072       // This usually will not affect the vertex positions, but it
02073       // could if the decal base has a transform and the decal child
02074       // is an instance node.  So don't do that.
02075       pvector<EggGroup *>::iterator di;
02076       for (di = decal_children.begin(); di != decal_children.end(); ++di) {
02077         EggGroup *child_group = (*di);
02078         decal_base->add_child(child_group);
02079       }
02080 
02081       // Also set the decal state on the base.
02082       decal_base->set_decal_flag(true);
02083     }
02084   }
02085 
02086   // Now recurse on each of the child nodes.
02087   for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
02088     EggNode *child =  (*ci);
02089     if (child->is_of_type(EggGroupNode::get_class_type())) {
02090       EggGroupNode *child_group = DCAST(EggGroupNode, child);
02091       if (!reparent_decals(child_group)) {
02092         okflag = false;
02093       }
02094     }
02095   }
02096 
02097   return okflag;
02098 }
02099 
02100 ////////////////////////////////////////////////////////////////////
02101 //     Function: MayaShader::string_transform_type
02102 //       Access: Public, Static
02103 //  Description: Returns the TransformType value corresponding to the
02104 //               indicated string, or TT_invalid.
02105 ////////////////////////////////////////////////////////////////////
02106 MayaToEggConverter::TransformType MayaToEggConverter::
02107 string_transform_type(const string &arg) {
02108   if (cmp_nocase(arg, "all") == 0) {
02109     return TT_all;
02110   } else if (cmp_nocase(arg, "model") == 0) {
02111     return TT_model;
02112   } else if (cmp_nocase(arg, "dcs") == 0) {
02113     return TT_dcs;
02114   } else if (cmp_nocase(arg, "none") == 0) {
02115     return TT_none;
02116   } else {
02117     return TT_invalid;
02118   }
02119 }

Generated on Fri May 2 03:21:24 2003 for Panda-Tool by doxygen1.3