Panda3D

maxToEggConverter.cxx

00001 // Filename: maxToEggConverter.cxx
00002 // Created by Corey Revilla and Ken Strickland (6/22/03)
00003 // from mayaToEggConverter.cxx created by drose (10Nov99)
00004 //
00005 // Updated by Fei Wang, Carnegie Mellon University Entertainment
00006 // Technology Center student, 29Jul2009:  Fixed vertex color, 
00007 // animation hierarchy, texture swapping bugs; added collision choices to 
00008 // exporter.
00009 //
00010 // Updated by Andrew Gartner, Carnegie Mellon University Entertainment
00011 // Technology Center. 27Apr2010: Collision is now done through User Defined Properties
00012 // By default a plane without a standard material gets UV's as well 
00013 // as any object without a texture but with a standard material.
00014 // Point objects are now supported as "locators" for a point in space
00015 // within the egg.
00016 ////////////////////////////////////////////////////////////////////
00017 //
00018 // PANDA 3D SOFTWARE
00019 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00020 //
00021 // All use of this software is subject to the terms of the revised BSD
00022 // license.  You should have received a copy of this license along
00023 // with this source code in a file named "LICENSE."
00024 //
00025 ////////////////////////////////////////////////////////////////////
00026 
00027 #include "maxEgg.h"
00028 
00029 ////////////////////////////////////////////////////////////////////
00030 //     Function: MaxToEggConverter::Constructor
00031 //       Access: Public
00032 //  Description: 
00033 ////////////////////////////////////////////////////////////////////
00034 MaxToEggConverter::
00035 MaxToEggConverter()
00036 {
00037     reset();
00038 }
00039 
00040 ////////////////////////////////////////////////////////////////////
00041 //     Function: MaxToEggConverter::Destructor
00042 //       Access: Public, Virtual
00043 //  Description: 
00044 ////////////////////////////////////////////////////////////////////
00045 MaxToEggConverter::
00046 ~MaxToEggConverter() 
00047 {
00048 }
00049 
00050 ////////////////////////////////////////////////////////////////////
00051 //     Function: MaxToEggConverter::reset
00052 ////////////////////////////////////////////////////////////////////
00053 void MaxToEggConverter::reset() {
00054     _cur_tref = 0;
00055     _current_frame = 0;
00056     _textures.clear();
00057     _egg_data = NULL;
00058 }
00059 
00060 ////////////////////////////////////////////////////////////////////
00061 //     Function: MaxToEggConverter::convert
00062 //       Access: Public
00063 //  Description: Fills up the egg_data structure according to the
00064 //               global Max model data.  Returns true if successful,
00065 //               false if there is an error.  If from_selection is
00066 //               true, the converted geometry is based on that which
00067 //               is selected; otherwise, it is the entire Max scene.
00068 ////////////////////////////////////////////////////////////////////
00069 bool MaxToEggConverter::convert(MaxEggOptions *options) {
00070 
00071     _options = options;
00072 
00073     Filename fn = Filename::from_os_specific(_options->_file_name);
00074     _options->_path_replace->_path_directory = fn.get_dirname();
00075 
00076     _egg_data = new EggData;
00077     if (_egg_data->get_coordinate_system() == CS_default) {
00078         _egg_data->set_coordinate_system(CS_zup_right);
00079     }
00080     
00081     // Figure out the animation parameters.
00082     
00083     // Get the start and end frames and the animation frame rate from Max
00084     
00085     Interval anim_range = _options->_max_interface->GetAnimRange();
00086     int start_frame = anim_range.Start()/GetTicksPerFrame();
00087     int end_frame = anim_range.End()/GetTicksPerFrame();
00088     
00089     if (!_options->_export_all_frames) {
00090         if (_options->_start_frame < start_frame) _options->_start_frame = start_frame;
00091         if (_options->_start_frame > end_frame)   _options->_start_frame = end_frame;
00092         if (_options->_end_frame < start_frame)   _options->_end_frame = start_frame;
00093         if (_options->_end_frame > end_frame)     _options->_end_frame = end_frame;
00094         if (_options->_end_frame < _options->_start_frame)  _options->_end_frame = _options->_start_frame;
00095         start_frame = _options->_start_frame;
00096         end_frame = _options->_end_frame;
00097     }
00098     
00099     int frame_inc = 1;
00100     int output_frame_rate = GetFrameRate();
00101 
00102     bool all_ok = true;
00103 
00104     if (_options->_export_whole_scene) {
00105         _tree._export_mesh = false;
00106         all_ok = _tree.build_complete_hierarchy(_options->_max_interface->GetRootNode(), NULL, 0);
00107     } else {
00108         _tree._export_mesh = true;
00109         all_ok = _tree.build_complete_hierarchy(_options->_max_interface->GetRootNode(), &_options->_node_list.front(), _options->_node_list.size());
00110     }
00111     
00112     if (all_ok) {
00113         switch (_options->_anim_type) {
00114         case MaxEggOptions::AT_pose:
00115             //pose: set to a specific frame, then get out the static geometry.
00116             //sprintf(Logger::GetLogString(), "Extracting geometry from frame #%d.", start_frame); 
00117              //Logger::Log( MTEC, Logger::SAT_MEDIUM_LEVEL, Logger::GetLogString() );
00118              //Logger::Log( MTEC, Logger::SAT_MEDIUM_LEVEL, "Converting static model." );
00119             _current_frame = start_frame;
00120             all_ok = convert_hierarchy(_egg_data);
00121             break;
00122             
00123         case MaxEggOptions::AT_model:
00124             // model: get out an animatable model with joints and vertex
00125             // membership.
00126             all_ok = convert_char_model();
00127             break;
00128             
00129         case MaxEggOptions::AT_chan:
00130             // chan: get out a series of animation tables.
00131             all_ok = convert_char_chan(start_frame, end_frame, frame_inc,
00132                                        output_frame_rate);
00133             break;
00134             
00135         case MaxEggOptions::AT_both:
00136             // both: Put a model and its animation into the same egg file.
00137             _options->_anim_type = MaxEggOptions::AT_model;
00138             if (!convert_char_model()) {
00139                 all_ok = false;
00140             }
00141             _options->_anim_type = MaxEggOptions::AT_chan;
00142             if (!convert_char_chan(start_frame, end_frame, frame_inc,
00143                                    output_frame_rate)) {
00144                 all_ok = false;
00145             }
00146             // Set the type back to AT_both
00147             _options->_anim_type = MaxEggOptions::AT_both;
00148             break;
00149           
00150           default:
00151             all_ok = false;
00152         };
00153         
00154         reparent_decals(_egg_data);
00155     }
00156     
00157     if (all_ok) {
00158         _egg_data->recompute_tangent_binormal_auto();
00159         _egg_data->remove_unused_vertices(true);
00160     }
00161     
00162     _options->_successful = all_ok;
00163     
00164     if (all_ok) {
00165         Filename fn = Filename::from_os_specific(_options->_file_name);
00166         return _egg_data->write_egg(fn);
00167     } else {
00168         return false;
00169     }
00170 }
00171 
00172 ////////////////////////////////////////////////////////////////////
00173 //     Function: MaxToEggConverter::convert_char_model
00174 //       Access: Private
00175 //  Description: Converts the file as an animatable character
00176 //               model, with joints and vertex membership.
00177 ////////////////////////////////////////////////////////////////////
00178 bool MaxToEggConverter::
00179 convert_char_model() {
00180     std::string character_name = "character";
00181     _current_frame = _options->_start_frame;
00182 
00183     EggGroup *char_node = new EggGroup(character_name);
00184     _egg_data->add_child(char_node);
00185     char_node->set_dart_type(EggGroup::DT_default);
00186     
00187     return convert_hierarchy(char_node);
00188 }
00189 
00190 ////////////////////////////////////////////////////////////////////
00191 //     Function: MaxToEggConverter::convert_char_chan
00192 //       Access: Private
00193 //  Description: Converts the animation as a series of tables to apply
00194 //               to the character model, as retrieved earlier via
00195 //               AC_model.
00196 ////////////////////////////////////////////////////////////////////
00197 bool MaxToEggConverter::
00198 convert_char_chan(double start_frame, double end_frame, double frame_inc,
00199                   double output_frame_rate) {
00200     std::string character_name = "character";
00201 
00202     EggTable *root_table_node = new EggTable();
00203     _egg_data->add_child(root_table_node);
00204     EggTable *bundle_node = new EggTable(character_name);
00205     bundle_node->set_table_type(EggTable::TT_bundle);
00206     root_table_node->add_child(bundle_node);
00207     EggTable *skeleton_node = new EggTable("<skeleton>");
00208     bundle_node->add_child(skeleton_node);
00209 
00210     // Set the frame rate before we start asking for anim tables to be
00211     // created.
00212     _tree._fps = output_frame_rate / frame_inc;
00213     _tree.clear_egg(_egg_data, NULL, skeleton_node);
00214     
00215     // Now we can get the animation data by walking through all of the
00216     // frames, one at a time, and getting the joint angles at each
00217     // frame.
00218     
00219     // This is just a temporary EggGroup to receive the transform for
00220     // each joint each frame.
00221     EggGroup* tgroup;
00222     
00223     int num_nodes = _tree.get_num_nodes();
00224     int i;
00225     
00226     TimeValue frame = start_frame;
00227     TimeValue frame_stop = end_frame;
00228     while (frame <= frame_stop) {
00229         _current_frame = frame;
00230         for (i = 0; i < num_nodes; i++) {
00231             // Find all joints in the hierarchy
00232             MaxNodeDesc *node_desc = _tree.get_node(i);
00233             if (node_desc->is_joint()) {
00234                 tgroup = new EggGroup();
00235                 INode *max_node = node_desc->get_max_node();
00236                 
00237                 if (node_desc->_parent && node_desc->_parent->is_joint()) {
00238                     // If this joint also has a joint as a parent, the parent's 
00239                     // transformation has to be divided out of this joint's TM
00240                     get_joint_transform(max_node, node_desc->_parent->get_max_node(), 
00241                                         tgroup);
00242                 } else {
00243                     get_joint_transform(max_node, NULL, tgroup);
00244                 }
00245                 
00246                 EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
00247                 if (!anim->add_data(tgroup->get_transform3d())) {
00248                     // *** log an error
00249                 }
00250                 delete tgroup;
00251             }
00252         }
00253         
00254         frame += frame_inc;
00255     }
00256     
00257     // Now optimize all of the tables we just filled up, for no real
00258     // good reason, except that it makes the resulting egg file a little
00259     // easier to read.
00260     for (i = 0; i < num_nodes; i++) {
00261         MaxNodeDesc *node_desc = _tree.get_node(i);
00262         if (node_desc->is_joint()) {
00263             _tree.get_egg_anim(node_desc)->optimize();
00264         }
00265     }
00266     
00267     return true;
00268 }
00269 
00270 ////////////////////////////////////////////////////////////////////
00271 //     Function: MaxToEggConverter::convert_hierarchy
00272 //       Access: Private
00273 //  Description: Generates egg structures for each node in the Max
00274 //               hierarchy.
00275 ////////////////////////////////////////////////////////////////////
00276 bool MaxToEggConverter::
00277 convert_hierarchy(EggGroupNode *egg_root) {
00278     //int num_nodes = _tree.get_num_nodes();
00279     
00280     _tree.clear_egg(_egg_data, egg_root, NULL);
00281     for (int i = 0; i < _tree.get_num_nodes(); i++) {
00282         if (!process_model_node(_tree.get_node(i))) {
00283             return false;
00284         }
00285     }
00286     
00287     return true;
00288 }
00289 
00290 ////////////////////////////////////////////////////////////////////
00291 //     Function: MaxToEggConverter::process_model_node
00292 //       Access: Private
00293 //  Description: Converts the indicated Max node to the
00294 //               corresponding Egg structure.  Returns true if
00295 //               successful, false if an error was encountered.
00296 ////////////////////////////////////////////////////////////////////
00297 bool MaxToEggConverter::
00298 process_model_node(MaxNodeDesc *node_desc) {
00299     if (!node_desc->has_max_node()) {
00300         // If the node has no Max equivalent, never mind.
00301         return true;
00302     }
00303 
00304     // Skip all nodes that represent joints in the geometry, but aren't 
00305     // the actual joints themselves
00306     if (node_desc->is_node_joint()) {
00307         return true;
00308     }
00309 
00310     TimeValue time = 0;
00311     INode *max_node = node_desc->get_max_node();
00312 
00313     ObjectState state;
00314     state = max_node->EvalWorldState(_current_frame * GetTicksPerFrame());
00315 
00316     if (node_desc->is_joint()) {
00317         EggGroup *egg_group = _tree.get_egg_group(node_desc);
00318         // Don't bother with joints unless we're getting an animatable
00319         // model.
00320         if (_options->_anim_type == MaxEggOptions::AT_model) { 
00321             get_joint_transform(max_node, egg_group);
00322         }
00323     } else {
00324         if (state.obj) {
00325             EggGroup *egg_group = NULL;
00326             TriObject *myMaxTriObject;
00327             Mesh max_mesh;
00328             //Call the correct exporter based on what type of object this is.
00329             switch( state.obj->SuperClassID() ){
00330 
00331             case GEOMOBJECT_CLASS_ID:
00332                 egg_group = _tree.get_egg_group(node_desc);
00333                 get_transform(max_node, egg_group);
00334     
00335                 //Try converting this geometric object to a mesh we can use.
00336                 if (!state.obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) {
00337                     return false;
00338                 } 
00339                 //Convert our state object to a TriObject.
00340                 myMaxTriObject = (TriObject *) state.obj->ConvertToType(time, Class_ID(TRIOBJ_CLASS_ID, 0 ));
00341                 // *** Want to figure this problem out 
00342                 // If actual conversion was required, then we want to delete this 
00343                 // new mesh later to avoid mem leaks. **BROKEN. doesnt delete
00344             
00345                 //Now, get the mesh.
00346                 max_mesh = myMaxTriObject->GetMesh();
00347                 make_polyset(max_node, &max_mesh, egg_group);
00348             
00349                 if (myMaxTriObject != state.obj)
00350                     delete myMaxTriObject;
00351                 break;
00352             
00353             case SHAPE_CLASS_ID:
00354                 if (state.obj->ClassID() == EDITABLE_SURF_CLASS_ID) {
00355                     NURBSSet getSet;
00356                     if (GetNURBSSet(state.obj, time, getSet, TRUE)) {
00357                         NURBSObject *nObj = getSet.GetNURBSObject(0);
00358                         if (nObj->GetType() == kNCVCurve) {
00359                             //It's a CV Curve, process it
00360                             egg_group = _tree.get_egg_group(node_desc);
00361                             get_transform(max_node, egg_group);
00362                             make_nurbs_curve((NURBSCVCurve *)nObj, string(max_node->GetName()),
00363                                              time, egg_group);
00364                         }
00365                     }
00366                 }
00367                 break;
00368 
00369             case CAMERA_CLASS_ID:
00370                 break;
00371           
00372             case LIGHT_CLASS_ID:
00373                 break;
00374           
00375             case HELPER_CLASS_ID:
00376               //we should export Point objects to give Max the equivalent of Maya locators
00377               if (state.obj->ClassID() == Class_ID(POINTHELP_CLASS_ID, 0)) {
00378                 
00379                 egg_group = _tree.get_egg_group(node_desc);
00380                 get_transform(max_node, egg_group);
00381 
00382               } else {
00383                 
00384                 break;
00385               
00386               }
00387               
00388                
00389               
00390 
00391             }
00392         }
00393     }
00394   
00395     return true;
00396 }
00397 
00398 ////////////////////////////////////////////////////////////////////
00399 //     Function: MaxToEggConverter::get_transform
00400 //       Access: Private
00401 //  Description: Extracts the transform on the indicated Maya node,
00402 //               and applies it to the corresponding Egg node.
00403 ////////////////////////////////////////////////////////////////////
00404 void MaxToEggConverter::
00405 get_transform(INode *max_node, EggGroup *egg_group) {
00406     if (_options->_anim_type == MaxEggOptions::AT_model) {
00407         // When we're getting an animated model, we only get transforms
00408         // for joints.
00409         return;
00410     }
00411 
00412     if ( !egg_group ) {
00413         return;
00414     }
00415 
00416     // Gets the TM for this node, a matrix which encapsulates all transformations
00417     // it takes to get to the current node, including parent transformations.
00418     Matrix3 pivot = max_node->GetNodeTM(_current_frame * GetTicksPerFrame());
00419 
00420     //This is the Panda-flava-flav-style matrix we'll be exporting to.
00421     Point3 row0 = pivot.GetRow(0);
00422     Point3 row1 = pivot.GetRow(1);
00423     Point3 row2 = pivot.GetRow(2);
00424     Point3 row3 = pivot.GetRow(3);
00425     
00426     LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
00427                   row1.x, row1.y, row1.z, 0.0f,
00428                   row2.x, row2.y, row2.z, 0.0f,
00429                   row3.x, row3.y, row3.z, 1.0f );
00430 
00431     // Now here's the tricky part. I believe this command strips out the node
00432     // "frame" which is the sum of all transformations enacted by the parent of
00433     // this node. This should reduce to the transformation relative to this 
00434     // node's parent
00435     m4d = m4d * egg_group->get_node_frame_inv();
00436     if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
00437         egg_group->add_matrix4(m4d);
00438     }
00439 }
00440 
00441 ////////////////////////////////////////////////////////////////////
00442 //     Function: MaxToEggConverter::get_object_transform
00443 //       Access: Private
00444 //  Description: Extracts the transform on the indicated Maya node,
00445 //               and applies it to the corresponding Egg node.
00446 ////////////////////////////////////////////////////////////////////
00447 LMatrix4d MaxToEggConverter::
00448 get_object_transform(INode *max_node) {
00449 
00450     // Gets the TM for this node, a matrix which encapsulates all transformations
00451     // it takes to get to the current node, including parent transformations.
00452     Matrix3 pivot = max_node->GetObjectTM(_current_frame * GetTicksPerFrame());
00453 
00454     Point3 row0 = pivot.GetRow(0);
00455     Point3 row1 = pivot.GetRow(1);
00456     Point3 row2 = pivot.GetRow(2);
00457     Point3 row3 = pivot.GetRow(3);
00458     
00459     LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
00460                   row1.x, row1.y, row1.z, 0.0f,
00461                   row2.x, row2.y, row2.z, 0.0f,
00462                   row3.x, row3.y, row3.z, 1.0f );
00463     return m4d;
00464 }
00465 
00466 ////////////////////////////////////////////////////////////////////
00467 //     Function: MaxToEggConverter::get_joint_transform
00468 //       Access: Private
00469 //  Description: Extracts the transform on the indicated Maya node,
00470 //               as appropriate for a joint in an animated character,
00471 //               and applies it to the indicated node.  This is
00472 //               different from get_transform() in that it does not
00473 //               respect the _transform_type flag, and it does not
00474 //               consider the relative transforms within the egg file.
00475 ////////////////////////////////////////////////////////////////////
00476 void MaxToEggConverter::
00477 get_joint_transform(INode *max_node, EggGroup *egg_group) {
00478 
00479     if ( !egg_group ) {
00480         return;
00481     }
00482 
00483     // Gets the TM for this node, a matrix which encapsulates all transformations
00484     // it takes to get to the current node, including parent transformations.
00485     Matrix3 pivot = max_node->GetNodeTM(_current_frame * GetTicksPerFrame());
00486     Point3 row0 = pivot.GetRow(0);
00487     Point3 row1 = pivot.GetRow(1);
00488     Point3 row2 = pivot.GetRow(2);
00489     Point3 row3 = pivot.GetRow(3);
00490 
00491     LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
00492                   row1.x, row1.y, row1.z, 0.0f,
00493                   row2.x, row2.y, row2.z, 0.0f,
00494                   row3.x, row3.y, row3.z, 1.0f );
00495 
00496     // Now here's the tricky part. I believe this command strips out the node
00497     // "frame" which is the sum of all transformations enacted by the parent of
00498     // this node. This should reduce to the transformation relative to this 
00499     // node's parent
00500     m4d = m4d * egg_group->get_node_frame_inv();
00501     if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
00502         egg_group->add_matrix4(m4d);
00503     }
00504 }
00505 
00506 ////////////////////////////////////////////////////////////////////
00507 //     Function: MaxToEggConverter::get_joint_transform
00508 //       Access: Private
00509 //  Description: Extracts the transform on the indicated Maya node,
00510 //               as appropriate for a joint in an animated character,
00511 //               and applies it to the indicated node.  This is
00512 //               different from get_transform() in that it does not
00513 //               respect the _transform_type flag, and it does not
00514 //               consider the relative transforms within the egg file.
00515 ////////////////////////////////////////////////////////////////////
00516 void MaxToEggConverter::
00517 get_joint_transform(INode *max_node, INode *parent_node, EggGroup *egg_group) {
00518 
00519     if ( !egg_group ) {
00520         return;
00521     }
00522 
00523     // Gets the TM for this node, a matrix which encapsulates all transformations
00524     // it takes to get to the current node, including parent transformations.
00525     Matrix3 pivot = max_node->GetNodeTM(_current_frame * GetTicksPerFrame());
00526     Point3 row0 = pivot.GetRow(0);
00527     Point3 row1 = pivot.GetRow(1);
00528     Point3 row2 = pivot.GetRow(2);
00529     Point3 row3 = pivot.GetRow(3);
00530 
00531     LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
00532                   row1.x, row1.y, row1.z, 0.0f,
00533                   row2.x, row2.y, row2.z, 0.0f,
00534                   row3.x, row3.y, row3.z, 1.0f );
00535 
00536     if (parent_node) {
00537         Matrix3 parent_pivot = parent_node->GetNodeTM(_current_frame * GetTicksPerFrame());
00538         //  parent_pivot.Invert();
00539         row0 = parent_pivot.GetRow(0);
00540         row1 = parent_pivot.GetRow(1);
00541         row2 = parent_pivot.GetRow(2);
00542         row3 = parent_pivot.GetRow(3);
00543 
00544         LMatrix4d pi_m4d(row0.x, row0.y, row0.z, 0.0f,
00545                          row1.x, row1.y, row1.z, 0.0f,
00546                          row2.x, row2.y, row2.z, 0.0f,
00547                          row3.x, row3.y, row3.z, 1.0f );
00548 
00549         // Now here's the tricky part. I believe this command strips out the node
00550         // "frame" which is the sum of all transformations enacted by the parent of
00551         // this node. This should reduce to the transformation relative to this 
00552         // node's parent
00553         pi_m4d.invert_in_place();
00554         m4d = m4d * pi_m4d;
00555     }
00556     if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
00557         egg_group->add_matrix4(m4d);
00558     }
00559 }
00560 
00561 ////////////////////////////////////////////////////////////////////
00562 //     Function: MaxToEggConverter::make_nurbs_curve
00563 //       Access: Private
00564 //  Description: Converts the indicated Maya NURBS curve (a standalone
00565 //               curve, not a trim curve) to a corresponding egg
00566 //               structure and attaches it to the indicated egg group.
00567 ////////////////////////////////////////////////////////////////////
00568 bool MaxToEggConverter::
00569 make_nurbs_curve(NURBSCVCurve *curve, const string &name,
00570                  TimeValue time, EggGroup *egg_group) 
00571 {
00572     int degree = curve->GetOrder();
00573     int cvs = curve->GetNumCVs();
00574     int knots = curve->GetNumKnots();
00575     int i;
00576 
00577     if (knots != cvs + degree) {
00578         return false;
00579     }
00580 
00581     string vpool_name = name + ".cvs";
00582     EggVertexPool *vpool = new EggVertexPool(vpool_name);
00583     egg_group->add_child(vpool);
00584 
00585     EggNurbsCurve *egg_curve = new EggNurbsCurve(name);
00586     egg_group->add_child(egg_curve);
00587     egg_curve->setup(degree, knots);
00588 
00589     for (i = 0; i < knots; i++)
00590         egg_curve->set_knot(i, curve->GetKnot(i));
00591 
00592     LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
00593 
00594     for (i = 0; i < cvs; i++) {
00595         NURBSControlVertex *cv = curve->GetCV(i);
00596         if (!cv) {
00597             char buf[1024];
00598             sprintf(buf, "Error getting CV %d", i);
00599             return false;
00600         } else {
00601             EggVertex vert;
00602             LPoint4d p4d(0, 0, 0, 1.0);
00603             cv->GetPosition(time, p4d[0], p4d[1], p4d[2]);
00604             p4d = p4d * vertex_frame_inv;
00605             vert.set_pos(p4d);
00606             egg_curve->add_vertex(vpool->create_unique_vertex(vert));
00607         }
00608     }
00609 
00610     return true;
00611 }
00612 
00613 ////////////////////////////////////////////////////////////////////
00614 //     Function: MaxToEggConverter::make_polyset
00615 //       Access: Private
00616 //  Description: Converts the indicated Maya polyset to a bunch of
00617 //               EggPolygons and parents them to the indicated egg
00618 //               group.
00619 ////////////////////////////////////////////////////////////////////
00620 void MaxToEggConverter::
00621 make_polyset(INode *max_node, Mesh *mesh,
00622              EggGroup *egg_group, Shader *default_shader) {
00623 
00624     mesh->buildNormals();
00625 
00626     if (mesh->getNumFaces() == 0) {
00627         return;
00628     }
00629 
00630     // One way to convert the mesh would be to first get out all the
00631     // vertices in the mesh and add them into the vpool, then when we
00632     // traverse the polygons we would only have to index them into the
00633     // vpool according to their Maya vertex index.
00634 
00635     // Unfortunately, since Maya may store multiple normals and/or
00636     // colors for each vertex according to which polygon it is in, that
00637     // approach won't necessarily work.  In egg, those split-property
00638     // vertices have to become separate vertices.  So instead of adding
00639     // all the vertices up front, we'll start with an empty vpool, and
00640     // add vertices to it on the fly.
00641 
00642     string vpool_name = string(max_node->GetName()) + ".verts";
00643     EggVertexPool *vpool = new EggVertexPool(vpool_name);
00644     egg_group->add_child(vpool);
00645 
00646     // We will need to transform all vertices from world coordinate
00647     // space into the vertex space appropriate to this node.  Usually,
00648     // this is the same thing as world coordinate space, and this matrix
00649     // will be identity; but if the node is under an instance
00650     // (particularly, for instance, a billboard) then the vertex space
00651     // will be different from world space.
00652     LMatrix4d vertex_frame = get_object_transform(max_node) * 
00653         egg_group->get_vertex_frame_inv();
00654 
00655 
00656     for ( int iFace=0; iFace < mesh->getNumFaces(); iFace++ ) {
00657         EggPolygon *egg_poly = new EggPolygon;
00658         egg_group->add_child(egg_poly);
00659 
00660         egg_poly->set_bface_flag(_options->_double_sided);
00661 
00662         Face face = mesh->faces[iFace];
00663 
00664         const PandaMaterial &pmat = get_panda_material(max_node->GetMtl(), face.getMatID());
00665 
00666         // Get the vertices for the polygon.
00667         for ( int iVertex=0; iVertex < 3; iVertex++ ) {
00668             EggVertex vert;
00669 
00670             // Get the vertex position
00671             Point3 vertex = mesh->getVert(face.v[iVertex]);
00672             LPoint3d p3d(vertex.x, vertex.y, vertex.z);
00673             p3d = p3d * vertex_frame;
00674             vert.set_pos(p3d);
00675 
00676             // Get the vertex normal
00677             Point3 normal = get_max_vertex_normal(mesh, iFace, iVertex);
00678             LVector3d n3d(normal.x, normal.y, normal.z);
00679             // *** Not quite sure if this transform should be applied, but it may 
00680             //     explain why normals were weird previously
00681             n3d = n3d * vertex_frame;
00682             vert.set_normal(n3d);
00683 
00684             // Get the vertex color
00685             if(mesh->vcFace)  // if has vcFace, has used vertex color
00686             {
00687                 VertColor vertexColor = get_max_vertex_color(mesh, iFace, iVertex);
00688                 LColor pVC(vertexColor.x, vertexColor.y, vertexColor.z, 1);
00689                 vert.set_color(pVC);
00690             }
00691             // Get the UVs for this vertex
00692 
00693             //first check if we returned nothing in the channels slot
00694             //we need UV's even in this case
00695             //because the user may not have put a material
00696             //on the object at all
00697             if (pmat._map_channels.size() == 0) {
00698               //since the channel will always be one because there's
00699               //no other textures then don't bother with the name
00700               UVVert uvw = get_max_vertex_texcoord(mesh, iFace, iVertex, 1);
00701               vert.set_uv( LTexCoordd(uvw.x, uvw.y));   
00702             }
00703             //otherwise go through and generate the maps per channel
00704             //this will also generate default UV's as long 
00705             //as the user applies a standard material to the object
00706             for (int iChan=0; iChan<pmat._map_channels.size(); iChan++) {
00707                 int channel = pmat._map_channels[iChan];
00708                 ostringstream uvname;
00709                 uvname << "m" << channel;
00710                 UVVert uvw = get_max_vertex_texcoord(mesh, iFace, iVertex, channel);
00711                 // changes allow the first channel to be swapped
00712                 if(channel == 1)
00713                     vert.set_uv( LTexCoordd(uvw.x, uvw.y));
00714                 else
00715                     vert.set_uv( uvname.str(), LTexCoordd(uvw.x, uvw.y));
00716            
00717             }
00718 
00719             vert.set_external_index(face.v[iVertex]);
00720 
00721             egg_poly->add_vertex(vpool->create_unique_vertex(vert));
00722         }
00723 
00724         //Max uses normals, not winding, to determine which way a 
00725         //polygon faces. Make sure the winding and that normal agree
00726         
00727         EggVertex *verts[3];
00728         LPoint3d points[3];
00729         
00730         for (int i = 0; i < 3; i++) {
00731             verts[i] = egg_poly->get_vertex(i);
00732             points[i] = verts[i]->get_pos3();
00733         }
00734 
00735         LVector3d realNorm = ((points[1] - points[0]).cross(points[2] - points[0]));
00736         Point3 maxNormTemp = mesh->getFaceNormal(iFace);
00737         LVector3d maxNorm = (LVector3d(maxNormTemp.x, maxNormTemp.y, maxNormTemp.z) *
00738                              vertex_frame);
00739 
00740         if (realNorm.dot(maxNorm) < 0.0) {
00741             egg_poly->set_vertex(0, verts[2]);
00742             egg_poly->set_vertex(2, verts[0]);
00743         }
00744 
00745         for (int i=0; i<pmat._texture_list.size(); i++) {
00746             egg_poly->add_texture(pmat._texture_list[i]);
00747         }
00748         egg_poly->set_color(pmat._color);
00749         
00750 
00751     }
00752     
00753     // Now that we've added all the polygons (and created all the
00754     // vertices), go back through the vertex pool and set up the
00755     // appropriate joint membership for each of the vertices.
00756     
00757     if (_options->_anim_type == MaxEggOptions::AT_model) {
00758         get_vertex_weights(max_node, vpool);
00759     }
00760 }
00761 
00762 UVVert MaxToEggConverter::get_max_vertex_texcoord(Mesh *mesh, int faceNo, int vertNo, int channel) {
00763 
00764     // extract the texture coordinate
00765     UVVert uvVert(0,0,0);
00766     if(mesh->mapSupport(channel)) {
00767         TVFace *pTVFace = mesh->mapFaces(channel);
00768         UVVert *pUVVert = mesh->mapVerts(channel);
00769         uvVert = pUVVert[pTVFace[faceNo].t[vertNo]];
00770     } else if(mesh->numTVerts > 0) {
00771         uvVert = mesh->tVerts[mesh->tvFace[faceNo].t[vertNo]];
00772     }
00773     return uvVert;
00774 }
00775 
00776 VertColor MaxToEggConverter::get_max_vertex_color(Mesh *mesh,int FaceNo,int VertexNo, int channel) {
00777 
00778   VertColor vc(0,0,0);
00779   if(mesh->mapSupport(channel))
00780   {
00781     // We get the color from vcFace
00782     TVFace& _vcface = mesh->vcFace[FaceNo];
00783     //Get its index into the vertCol array
00784     int VertexColorIndex = _vcface.t[VertexNo];
00785     //Get its color
00786     vc =mesh->vertCol[VertexColorIndex];
00787   }
00788   else
00789   {
00790     TVFace *pTVFace = mesh->mapFaces(channel);
00791     vc = mesh->vertCol[pTVFace[FaceNo].t[VertexNo]];
00792   }
00793   return vc;
00794 }
00795 
00796 VertColor MaxToEggConverter::get_max_vertex_color(Mesh *mesh,int FaceNo,int VertexNo)
00797 {
00798     VertColor vc(0,0,0);
00799     // We get the color from vcFace
00800     TVFace& _vcface = mesh->vcFace[FaceNo];
00801     //Get its index into the vertCol array
00802     int VertexColorIndex = _vcface.t[VertexNo];
00803     //Get its color
00804     vc =mesh->vertCol[VertexColorIndex];
00805     return vc;
00806 }
00807     
00808 Point3 MaxToEggConverter::get_max_vertex_normal(Mesh *mesh, int faceNo, int vertNo)
00809 {
00810     Face f = mesh->faces[faceNo];
00811     DWORD smGroup = f.smGroup;
00812     int vert = f.getVert(vertNo);
00813     RVertex *rv = mesh->getRVertPtr(vert);
00814   
00815     int numNormals;
00816     Point3 vertexNormal;
00817 
00818     // Is normal specified
00819     // SPCIFIED is not currently used, but may be used in future versions.
00820     if (rv->rFlags & SPECIFIED_NORMAL) {
00821         vertexNormal = rv->rn.getNormal();
00822     }
00823     // If normal is not specified it's only available if the face belongs
00824     // to a smoothing group
00825     else if ((numNormals = rv->rFlags & NORCT_MASK) && smGroup) {
00826         // If there is only one vertex is found in the rn member.
00827         if (numNormals == 1) {
00828             vertexNormal = rv->rn.getNormal();
00829         }
00830         else {
00831             // If two or more vertices are there you need to step through them
00832             // and find the vertex with the same smoothing group as the current face.
00833             // You will find multiple normals in the ern member.
00834             for (int i = 0; i < numNormals; i++) {
00835                 if (rv->ern[i].getSmGroup() & smGroup) {
00836                     vertexNormal = rv->ern[i].getNormal();
00837                 }
00838             }
00839         }
00840     }
00841     else {
00842         // Get the normal from the Face if no smoothing groups are there
00843         vertexNormal = mesh->getFaceNormal(faceNo);
00844     }
00845   
00846     return vertexNormal;
00847 }
00848 
00849 ////////////////////////////////////////////////////////////////////
00850 //     Function: MaxToEggConverter::get_vertex_weights
00851 //       Access: Private
00852 //  Description: 
00853 ////////////////////////////////////////////////////////////////////
00854 void MaxToEggConverter::
00855 get_vertex_weights(INode *max_node, EggVertexPool *vpool) {
00856     //Try to get the weights out of a physique if one exists
00857     Modifier *mod = FindSkinModifier(max_node, PHYSIQUE_CLASSID);
00858     EggVertexPool::iterator vi;
00859 
00860     if (mod) {
00861         // create a physique export interface
00862         IPhysiqueExport *pPhysiqueExport = (IPhysiqueExport *)mod->GetInterface(I_PHYINTERFACE);
00863         if (pPhysiqueExport) {
00864             // create a context export interface
00865             IPhyContextExport *pContextExport = 
00866                 (IPhyContextExport *)pPhysiqueExport->GetContextInterface(max_node);
00867             if (pContextExport) {
00868                 // set the flags in the context export interface
00869                 pContextExport->ConvertToRigid(TRUE);
00870                 pContextExport->AllowBlending(TRUE);
00871   
00872                 for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
00873                     EggVertex *vert = (*vi);
00874                     int max_vi = vert->get_external_index();
00875 
00876                     // get the vertex export interface
00877                     IPhyVertexExport *pVertexExport = 
00878                         (IPhyVertexExport *)pContextExport->GetVertexInterface(max_vi);
00879                     if (pVertexExport) {
00880                         int vertexType = pVertexExport->GetVertexType();
00881 
00882                         // handle the specific vertex type
00883                         if(vertexType == RIGID_TYPE) {
00884                             // typecast to rigid vertex
00885                             IPhyRigidVertex *pTypeVertex = (IPhyRigidVertex *)pVertexExport;
00886                             INode *bone_node = pTypeVertex->GetNode();
00887                             MaxNodeDesc *joint_node_desc = _tree.find_joint(bone_node);
00888                             if (joint_node_desc){
00889                                 EggGroup *joint = _tree.get_egg_group(joint_node_desc);
00890                                 if (joint != (EggGroup *)NULL)
00891                                     joint->ref_vertex(vert, 1.0f);
00892                             }
00893                         }
00894                         else if(vertexType == RIGID_BLENDED_TYPE) {
00895                             // typecast to blended vertex
00896                             IPhyBlendedRigidVertex *pTypeVertex = (IPhyBlendedRigidVertex *)pVertexExport;
00897 
00898                             for (int ji = 0; ji < pTypeVertex->GetNumberNodes(); ++ji) {
00899                                 PN_stdfloat weight = pTypeVertex->GetWeight(ji);
00900                                 if (weight > 0.0f) {
00901                                     INode *bone_node = pTypeVertex->GetNode(ji);
00902                                     MaxNodeDesc *joint_node_desc = _tree.find_joint(bone_node);
00903                                     if (joint_node_desc){
00904                                         EggGroup *joint = _tree.get_egg_group(joint_node_desc);
00905                                         if (joint != (EggGroup *)NULL)
00906                                             joint->ref_vertex(vert, weight);
00907                                     }
00908                                 }
00909                             }
00910                         }
00911                         //Release the vertex interface
00912                         pContextExport->ReleaseVertexInterface(pVertexExport);
00913                     }
00914                 }
00915                 //Release the context interface
00916                 pPhysiqueExport->ReleaseContextInterface(pContextExport);
00917             }
00918             //Release the physique export interface
00919             mod->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
00920         }
00921     }
00922     else {
00923         //No physique, try to find a skin
00924         mod = FindSkinModifier(max_node, SKIN_CLASSID);
00925         if (mod) {
00926             ISkin *skin = (ISkin*)mod->GetInterface(I_SKIN);
00927             if (skin) {
00928                 ISkinContextData *skinMC = skin->GetContextInterface(max_node);
00929                 if (skinMC) {
00930                     for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
00931                         EggVertex *vert = (*vi);
00932                         int max_vi = vert->get_external_index();
00933   
00934                         for (int ji = 0; ji < skinMC->GetNumAssignedBones(max_vi); ++ji) {
00935                             PN_stdfloat weight = skinMC->GetBoneWeight(max_vi, ji);
00936                             if (weight > 0.0f) {
00937                                 INode *bone_node = skin->GetBone(skinMC->GetAssignedBone(max_vi, ji));
00938                                 MaxNodeDesc *joint_node_desc = _tree.find_joint(bone_node);
00939                                 if (joint_node_desc){
00940                                     EggGroup *joint = _tree.get_egg_group(joint_node_desc);
00941                                     if (joint != (EggGroup *)NULL) {
00942                                         joint->ref_vertex(vert, weight);
00943                                     }
00944                                 }
00945                             }
00946                         }
00947                     }
00948                 }
00949             }
00950         }
00951     }
00952 }
00953 
00954 
00955 ////////////////////////////////////////////////////////////////////
00956 //     Function: MaxToEggConverter::get_material_textures
00957 //       Access: Private
00958 //  Description: Converts a Max material into a set of Panda textures
00959 //               and a primitive color.
00960 ////////////////////////////////////////////////////////////////////
00961 const MaxToEggConverter::PandaMaterial &MaxToEggConverter::
00962 get_panda_material(Mtl *mtl, MtlID matID) {
00963 
00964     MaterialMap::iterator it = _material_map.find(mtl);
00965     if (it != _material_map.end()) {
00966         return (*it).second;
00967     }
00968     
00969     PandaMaterial &pandaMat = _material_map[mtl];
00970     pandaMat._color = LColor(1,1,1,1);
00971     pandaMat._any_diffuse = false;
00972     pandaMat._any_opacity = false;
00973     pandaMat._any_gloss = false;
00974     pandaMat._any_normal = false;
00975     
00976    
00977    
00978 
00979     // If it's a multi-material, dig down.
00980         
00981     while (( mtl != 0) && (mtl->ClassID() == Class_ID(MULTI_CLASS_ID, 0 ))) {
00982         if (matID < mtl->NumSubMtls()) {
00983             mtl = mtl->GetSubMtl(matID);
00984         } else {
00985             mtl = 0;
00986         }
00987     }
00988 
00989     // If it's a standard material, we're good.
00990     
00991     if ((mtl != 0) && (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0 ))) {
00992         StdMat *maxMaterial = (StdMat*)mtl;
00993         analyze_diffuse_maps(pandaMat, maxMaterial->GetSubTexmap(ID_DI));
00994         analyze_opacity_maps(pandaMat, maxMaterial->GetSubTexmap(ID_OP));
00995         analyze_gloss_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SP));
00996         if (!pandaMat._any_gloss)
00997             analyze_gloss_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SS));
00998         if (!pandaMat._any_gloss)
00999             analyze_gloss_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SH));
01000         analyze_glow_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SI));
01001         analyze_normal_maps(pandaMat, maxMaterial->GetSubTexmap(ID_BU));
01002         for (int i=0; i<pandaMat._texture_list.size(); i++) {
01003             EggTexture *src = pandaMat._texture_list[i];
01004             pandaMat._texture_list[i] =
01005                 _textures.create_unique_texture(*src, ~EggTexture::E_tref_name);
01006         }
01007 
01008         // The existence of a texture on either color channel completely
01009         // replaces the corresponding flat color.
01010         if (!pandaMat._any_diffuse) {
01011             // Get the default diffuse color of the material without the texture map
01012             Point3 diffuseColor = Point3(maxMaterial->GetDiffuse(0));
01013             pandaMat._color[0] = diffuseColor.x;
01014             pandaMat._color[1] = diffuseColor.y;
01015             pandaMat._color[2] = diffuseColor.z;
01016         }
01017         if (!pandaMat._any_opacity) {
01018             
01019             pandaMat._color[3] = (maxMaterial->GetOpacity(_current_frame * GetTicksPerFrame()));
01020         }
01021         if (pandaMat._texture_list.size() < 1) {
01022             //if we don't have any maps whatsoever, 
01023             //give the material a dummy channel 
01024             //so that UV's get created
01025             pandaMat._map_channels.push_back(1);
01026         }
01027         return pandaMat;
01028     }
01029       
01030     // Otherwise, it's unrecognizable. Leave result blank.
01031     return pandaMat;
01032 }
01033 ////////////////////////////////////////////////////////////////////
01034 //     Function: MayaShader::analyze_diffuse_maps
01035 //       Access: Private
01036 //  Description: 
01037 ////////////////////////////////////////////////////////////////////
01038 void MaxToEggConverter::analyze_diffuse_maps(PandaMaterial &pandaMat, Texmap *mat) {
01039     if (mat == 0) return;
01040     
01041     if (mat->ClassID() == Class_ID(RGBMULT_CLASS_ID, 0)) {
01042         for (int i=0; i<mat->NumSubTexmaps(); i++) {
01043             analyze_diffuse_maps(pandaMat, mat->GetSubTexmap(i));
01044         }
01045         return;
01046     }
01047 
01048     if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
01049         pandaMat._any_diffuse = true;
01050         PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
01051 
01052         BitmapTex *diffuseTex = (BitmapTex *)mat;
01053 
01054         Filename fullpath, outpath;
01055         Filename filename = Filename::from_os_specific(diffuseTex->GetMapName());
01056         _options->_path_replace->full_convert_path(filename, get_model_path(),
01057                                                    fullpath, outpath);
01058         tex->set_filename(outpath);
01059         tex->set_fullpath(fullpath);
01060 
01061         apply_texture_properties(*tex, diffuseTex->GetMapChannel());
01062         add_map_channel(pandaMat, diffuseTex->GetMapChannel());
01063         
01064         Bitmap *diffuseBitmap = diffuseTex->GetBitmap(0);
01065         if ( diffuseBitmap && diffuseBitmap->HasAlpha()) {
01066             tex->set_format(EggTexture::F_rgba);
01067         } else {
01068             tex->set_format(EggTexture::F_rgb);
01069         }
01070         tex->set_env_type(EggTexture::ET_modulate);
01071         
01072         pandaMat._texture_list.push_back(tex);
01073     }
01074 }
01075 
01076 ////////////////////////////////////////////////////////////////////
01077 //     Function: MayaShader::analyze_opacity_maps
01078 //       Access: Private
01079 //  Description: 
01080 ////////////////////////////////////////////////////////////////////
01081 void MaxToEggConverter::analyze_opacity_maps(PandaMaterial &pandaMat, Texmap *mat) {
01082     if (mat == 0) return;
01083     
01084     if (mat->ClassID() == Class_ID(RGBMULT_CLASS_ID, 0)) {
01085         for (int i=0; i<mat->NumSubTexmaps(); i++) {
01086             analyze_opacity_maps(pandaMat, mat->GetSubTexmap(i));
01087         }
01088         return;
01089     }
01090 
01091     if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
01092         pandaMat._any_opacity = true;
01093         BitmapTex *transTex = (BitmapTex *)mat;
01094 
01095         Filename fullpath, outpath;
01096         Filename filename = Filename::from_os_specific(transTex->GetMapName());
01097         _options->_path_replace->full_convert_path(filename, get_model_path(),
01098                                                    fullpath, outpath);
01099 
01100         // See if this opacity map already showed up.
01101         for (int i=0; i<pandaMat._texture_list.size(); i++) {
01102             EggTexture *tex = pandaMat._texture_list[i];
01103             if ((tex->get_env_type()==EggTexture::ET_modulate)&&(tex->get_fullpath() == fullpath)) {
01104                 tex->set_format(EggTexture::F_rgba);
01105                 return;
01106             }
01107         }
01108         
01109         // Try to find a diffuse map to pair this with as an alpha-texture.
01110         std::string uvname = get_uv_name(transTex->GetMapChannel());
01111         for (int i=0; i<pandaMat._texture_list.size(); i++) {
01112             EggTexture *tex = pandaMat._texture_list[i];
01113             if ((tex->get_env_type()==EggTexture::ET_modulate)&&
01114                 (tex->get_format() == EggTexture::F_rgb)&&
01115                 (tex->get_uv_name() == uvname)) {
01116                 tex->set_format(EggTexture::F_rgba);
01117                 tex->set_alpha_filename(outpath);
01118                 tex->set_alpha_fullpath(fullpath);
01119                 return;
01120             }
01121         }
01122         
01123         // Otherwise, just create it as an alpha-texture.
01124         PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
01125         tex->set_filename(outpath);
01126         tex->set_fullpath(fullpath);
01127 
01128         apply_texture_properties(*tex, transTex->GetMapChannel());
01129         add_map_channel(pandaMat, transTex->GetMapChannel());
01130         tex->set_format(EggTexture::F_alpha);
01131 
01132         pandaMat._texture_list.push_back(tex);
01133     }
01134 }
01135 
01136 ////////////////////////////////////////////////////////////////////
01137 //     Function: MayaShader::analyze_glow_maps
01138 //       Access: Private
01139 //  Description: 
01140 ////////////////////////////////////////////////////////////////////
01141 void MaxToEggConverter::analyze_glow_maps(PandaMaterial &pandaMat, Texmap *mat) {
01142     if (mat == 0) return;
01143     
01144     if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
01145         BitmapTex *gtex = (BitmapTex *)mat;
01146 
01147         Filename fullpath, outpath;
01148         Filename filename = Filename::from_os_specific(gtex->GetMapName());
01149         _options->_path_replace->full_convert_path(filename, get_model_path(),
01150                                                    fullpath, outpath);
01151 
01152         // Try to find a diffuse map to pair this with as an alpha-texture.
01153         std::string uvname = get_uv_name(gtex->GetMapChannel());
01154         for (int i=0; i<pandaMat._texture_list.size(); i++) {
01155             EggTexture *tex = pandaMat._texture_list[i];
01156             if ((tex->get_env_type()==EggTexture::ET_modulate)&&
01157                 (tex->get_format() == EggTexture::F_rgb)&&
01158                 (tex->get_uv_name() == uvname)) {
01159                 tex->set_env_type(EggTexture::ET_modulate_glow);
01160                 tex->set_format(EggTexture::F_rgba);
01161                 tex->set_alpha_filename(outpath);
01162                 tex->set_alpha_fullpath(fullpath);
01163                 return;
01164             }
01165         }
01166         
01167         // Otherwise, just create it as a separate glow-texture.
01168         PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
01169         tex->set_env_type(EggTexture::ET_glow);
01170         tex->set_filename(outpath);
01171         tex->set_fullpath(fullpath);
01172         apply_texture_properties(*tex, gtex->GetMapChannel());
01173         add_map_channel(pandaMat, gtex->GetMapChannel());
01174         tex->set_format(EggTexture::F_alpha);
01175         
01176         pandaMat._texture_list.push_back(tex);
01177     }
01178 }
01179 
01180 ////////////////////////////////////////////////////////////////////
01181 //     Function: MayaShader::analyze_gloss_maps
01182 //       Access: Private
01183 //  Description: 
01184 ////////////////////////////////////////////////////////////////////
01185 void MaxToEggConverter::analyze_gloss_maps(PandaMaterial &pandaMat, Texmap *mat) {
01186     if (mat == 0) return;
01187     
01188     if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
01189         pandaMat._any_gloss = true;
01190         BitmapTex *gtex = (BitmapTex *)mat;
01191 
01192         Filename fullpath, outpath;
01193         Filename filename = Filename::from_os_specific(gtex->GetMapName());
01194         _options->_path_replace->full_convert_path(filename, get_model_path(),
01195                                                    fullpath, outpath);
01196 
01197         // Try to find a diffuse map to pair this with as an alpha-texture.
01198         std::string uvname = get_uv_name(gtex->GetMapChannel());
01199         for (int i=0; i<pandaMat._texture_list.size(); i++) {
01200             EggTexture *tex = pandaMat._texture_list[i];
01201             if ((tex->get_env_type()==EggTexture::ET_modulate)&&
01202                 (tex->get_format() == EggTexture::F_rgb)&&
01203                 (tex->get_uv_name() == uvname)) {
01204                 tex->set_env_type(EggTexture::ET_modulate_gloss);
01205                 tex->set_format(EggTexture::F_rgba);
01206                 tex->set_alpha_filename(outpath);
01207                 tex->set_alpha_fullpath(fullpath);
01208                 return;
01209             }
01210         }
01211         
01212         // Otherwise, just create it as a separate gloss-texture.
01213         PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
01214         tex->set_env_type(EggTexture::ET_gloss);
01215         tex->set_filename(outpath);
01216         tex->set_fullpath(fullpath);
01217         apply_texture_properties(*tex, gtex->GetMapChannel());
01218         add_map_channel(pandaMat, gtex->GetMapChannel());
01219         tex->set_format(EggTexture::F_alpha);
01220         
01221         pandaMat._texture_list.push_back(tex);
01222     }
01223 }
01224 
01225 ////////////////////////////////////////////////////////////////////
01226 //     Function: MayaShader::analyze_normal_maps
01227 //       Access: Private
01228 //  Description: 
01229 ////////////////////////////////////////////////////////////////////
01230 void MaxToEggConverter::analyze_normal_maps(PandaMaterial &pandaMat, Texmap *mat) {
01231     if (mat == 0) return;
01232     
01233     if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
01234         pandaMat._any_normal = true;
01235         BitmapTex *ntex = (BitmapTex *)mat;
01236 
01237         Filename fullpath, outpath;
01238         Filename filename = Filename::from_os_specific(ntex->GetMapName());
01239         _options->_path_replace->full_convert_path(filename, get_model_path(),
01240                                                    fullpath, outpath);
01241 
01242         PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
01243         tex->set_env_type(EggTexture::ET_normal);
01244         tex->set_filename(outpath);
01245         tex->set_fullpath(fullpath);
01246         apply_texture_properties(*tex, ntex->GetMapChannel());
01247         add_map_channel(pandaMat, ntex->GetMapChannel());
01248         tex->set_format(EggTexture::F_rgb);
01249         
01250         pandaMat._texture_list.push_back(tex);
01251     }
01252 }
01253 
01254 ////////////////////////////////////////////////////////////////////
01255 //     Function: MayaShader::add_map_channel
01256 //       Access: Private
01257 //  Description: Adds the specified map channel to the map channel
01258 //               list, if it's not already there.
01259 ////////////////////////////////////////////////////////////////////
01260 void MaxToEggConverter::add_map_channel(PandaMaterial &pandaMat, int chan) {
01261     for (int i=0; i<pandaMat._map_channels.size(); i++) {
01262         if (pandaMat._map_channels[i] == chan) {
01263             return;
01264         }
01265     }
01266     pandaMat._map_channels.push_back(chan);
01267 }
01268 
01269 ////////////////////////////////////////////////////////////////////
01270 //     Function: MayaShader::generate_tex_name
01271 //       Access: Private
01272 //  Description: Generates an arbitrary unused texture name.
01273 ////////////////////////////////////////////////////////////////////
01274 std::string MaxToEggConverter::generate_tex_name() {
01275     ostringstream name_strm;
01276     name_strm << "Tex" << ++_cur_tref;
01277     return name_strm.str();
01278 }
01279 
01280 ////////////////////////////////////////////////////////////////////
01281 //     Function: MayaShader::get_uv_name
01282 //       Access: Private
01283 //  Description: Returns the UV-name of the nth map-channel.
01284 ////////////////////////////////////////////////////////////////////
01285 std::string MaxToEggConverter::get_uv_name(int channel) {
01286     ostringstream uvname;
01287     uvname << "m" << channel;
01288     return uvname.str();
01289 }
01290 
01291 ////////////////////////////////////////////////////////////////////
01292 //     Function: MayaShader::apply_texture_properties
01293 //       Access: Private
01294 //  Description: Applies all the appropriate texture properties to the
01295 //               EggTexture object, including wrap modes and texture
01296 //               matrix.
01297 ////////////////////////////////////////////////////////////////////
01298 void MaxToEggConverter::
01299 apply_texture_properties(EggTexture &tex, int channel) {
01300 
01301     // we leave a channel 1 for texture swapping, so don't name it
01302     if(channel == 1)
01303       tex.set_uv_name("");
01304     else
01305       tex.set_uv_name(get_uv_name(channel));
01306 
01307     tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
01308     tex.set_magfilter(EggTexture::FT_linear);
01309 
01310     EggTexture::WrapMode wrap_u = EggTexture::WM_repeat;
01311     EggTexture::WrapMode wrap_v = EggTexture::WM_repeat;
01312   
01313     tex.set_wrap_u(wrap_u);
01314     tex.set_wrap_v(wrap_v);
01315 }
01316 
01317 
01318 ////////////////////////////////////////////////////////////////////
01319 //     Function: MayaShader::reparent_decals
01320 //       Access: Private
01321 //  Description: Recursively walks the egg hierarchy, reparenting
01322 //               "decal" type nodes below their corresponding
01323 //               "decalbase" type nodes, and setting the flags.
01324 //
01325 //               Returns true on success, false if some nodes were
01326 //               incorrect.
01327 ////////////////////////////////////////////////////////////////////
01328 bool MaxToEggConverter::
01329 reparent_decals(EggGroupNode *egg_parent) {
01330     bool okflag = true;
01331 
01332     // First, walk through all children of this node, looking for the
01333     // one decal base, if any.
01334     EggGroup *decal_base = (EggGroup *)NULL;
01335     pvector<EggGroup *> decal_children;
01336 
01337     EggGroupNode::iterator ci;
01338     for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
01339         EggNode *child =  (*ci);
01340         if (child->is_of_type(EggGroup::get_class_type())) {
01341             EggGroup *child_group = (EggGroup *) child;
01342             if (child_group->has_object_type("decalbase")) {
01343                 if (decal_base != (EggNode *)NULL) {
01344                     // error
01345                     okflag = false;
01346                 }
01347                 child_group->remove_object_type("decalbase");
01348                 decal_base = child_group;
01349 
01350             } else if (child_group->has_object_type("decal")) {
01351                 child_group->remove_object_type("decal");
01352                 decal_children.push_back(child_group);
01353             }
01354         }
01355     }
01356 
01357     if (decal_base == (EggGroup *)NULL) {
01358         if (!decal_children.empty()) {
01359             // warning
01360         }
01361 
01362     } else {
01363         if (decal_children.empty()) {
01364             // warning
01365 
01366         } else {
01367             // All the decal children get moved to be a child of decal base.
01368             // This usually will not affect the vertex positions, but it
01369             // could if the decal base has a transform and the decal child
01370             // is an instance node.  So don't do that.
01371             pvector<EggGroup *>::iterator di;
01372             for (di = decal_children.begin(); di != decal_children.end(); ++di) {
01373                 EggGroup *child_group = (*di);
01374                 decal_base->add_child(child_group);
01375             }
01376 
01377             // Also set the decal state on the base.
01378             decal_base->set_decal_flag(true);
01379         }
01380     }
01381 
01382     // Now recurse on each of the child nodes.
01383     for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
01384         EggNode *child =  (*ci);
01385         if (child->is_of_type(EggGroupNode::get_class_type())) {
01386             EggGroupNode *child_group = (EggGroupNode *) child;
01387             if (!reparent_decals(child_group)) {
01388                 okflag = false;
01389             }
01390         }
01391     }
01392 
01393     return okflag;
01394 }
01395 
01396 Modifier* MaxToEggConverter::FindSkinModifier (INode* node, const Class_ID &type)
01397 {
01398     // Get object from node. Abort if no object.
01399     Object* pObj = node->GetObjectRef();
01400     if (!pObj) return NULL;
01401 
01402     // Is derived object ?
01403     while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID) {
01404         // Yes -> Cast.
01405         IDerivedObject* pDerObj = static_cast<IDerivedObject*>(pObj);
01406 
01407         // Iterate over all entries of the modifier stack.
01408         for (int stackId = 0; stackId < pDerObj->NumModifiers(); ++stackId) {
01409             // Get current modifier.
01410             Modifier* mod = pDerObj->GetModifier(stackId);
01411             
01412             // Is this what we are looking for?
01413             if (mod->ClassID() == type )
01414                 return mod;
01415         }
01416 
01417         // continue with next derived object
01418         pObj = pDerObj->GetObjRef();
01419     }
01420 
01421     // Not found.
01422     return NULL;
01423 }
 All Classes Functions Variables Enumerations