Panda3D
|
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 }