Panda3D
|
00001 // Filename: eggOptchar.cxx 00002 // Created by: drose (18Jul03) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "eggOptchar.h" 00016 #include "eggOptcharUserData.h" 00017 #include "vertexMembership.h" 00018 00019 #include "eggJointData.h" 00020 #include "eggSliderData.h" 00021 #include "eggCharacterCollection.h" 00022 #include "eggCharacterData.h" 00023 #include "eggBackPointer.h" 00024 #include "eggGroupNode.h" 00025 #include "eggPrimitive.h" 00026 #include "eggVertexPool.h" 00027 #include "eggTable.h" 00028 #include "eggGroup.h" 00029 #include "eggAnimPreload.h" 00030 #include "string_utils.h" 00031 #include "dcast.h" 00032 #include "pset.h" 00033 #include "compose_matrix.h" 00034 #include "fftCompressor.h" 00035 #include "pystub.h" 00036 00037 #include <algorithm> 00038 00039 //////////////////////////////////////////////////////////////////// 00040 // Function: EggOptchar::Constructor 00041 // Access: Public 00042 // Description: 00043 //////////////////////////////////////////////////////////////////// 00044 EggOptchar:: 00045 EggOptchar() { 00046 add_path_replace_options(); 00047 add_path_store_options(); 00048 add_normals_options(); 00049 add_transform_options(); 00050 add_fixrest_option(); 00051 00052 set_program_description 00053 ("egg-optchar performs basic optimizations of a character model " 00054 "and its associated animations, primarily by analyzing the " 00055 "animation tables and removing unneeded joints and/or morphs. " 00056 "It can also perform basic restructuring operations on the " 00057 "character hierarchy."); 00058 00059 add_option 00060 ("ls", "", 0, 00061 "List the joint hierarchy instead of performing any operations.", 00062 &EggOptchar::dispatch_none, &_list_hierarchy); 00063 00064 add_option 00065 ("lsv", "", 0, 00066 "List the joint hierarchy along with an indication of the properties " 00067 "each joint.", 00068 &EggOptchar::dispatch_none, &_list_hierarchy_v); 00069 00070 add_option 00071 ("lsp", "", 0, 00072 "List the existing joint hierarchy as a series of -p joint,parent " 00073 "commands, suitable for pasting into an egg-optchar command line.", 00074 &EggOptchar::dispatch_none, &_list_hierarchy_p); 00075 00076 add_option 00077 ("keep", "joint[,joint...]", 0, 00078 "Keep the named joints (or sliders) in the character, even if they do " 00079 "not appear to be needed by the animation.", 00080 &EggOptchar::dispatch_vector_string_comma, NULL, &_keep_components); 00081 00082 add_option 00083 ("drop", "joint[,joint...]", 0, 00084 "Removes the named joints or sliders, even if they appear to be needed.", 00085 &EggOptchar::dispatch_vector_string_comma, NULL, &_drop_components); 00086 00087 add_option 00088 ("expose", "joint[,joint...]", 0, 00089 "Expose the named joints by flagging them with a DCS attribute, so " 00090 "each one can be found in the scene graph when the character is loaded, " 00091 "and objects can be parented to it. This implies -keep.", 00092 &EggOptchar::dispatch_vector_string_comma, NULL, &_expose_components); 00093 00094 add_option 00095 ("suppress", "joint[,joint...]", 0, 00096 "The opposite of suppress, this prevents the named joints from " 00097 "being created with an implicit DCS attribute, even if they contain " 00098 "rigid geometry. The default is to create an implicit node for any " 00099 "joint that contains rigid geometry, to take advantage of display " 00100 "list and/or vertex buffer caching. This does not imply -keep.", 00101 &EggOptchar::dispatch_vector_string_comma, NULL, &_suppress_components); 00102 00103 add_option 00104 ("flag", "node[,node...][=name]", 0, 00105 "Assign the indicated name to the geometry within the given nodes. " 00106 "This will make the geometry visible as a node in the resulting " 00107 "character model when it is loaded in the scene graph (normally, " 00108 "the node hierarchy is suppressed when loading characters). This " 00109 "is different from -expose in that it reveals geometry rather than " 00110 "joints; the revealed node can be hidden or its attributes changed " 00111 "at runtime, but it will be animated by its vertices, not the node, so " 00112 "objects parented to this node will not inherit its animation.", 00113 &EggOptchar::dispatch_flag_groups, NULL, &_flag_groups); 00114 00115 add_option 00116 ("defpose", "anim.egg,frame", 0, 00117 "Specify the model's default pose. The pose is taken " 00118 "from the indicated frame of the named animation file (which must " 00119 "also be named separately on the command line). The " 00120 "pose will be held by the model in " 00121 "the absence of any animation, and need not be the same " 00122 "pose in which the model was originally skinned.", 00123 &EggOptchar::dispatch_string, NULL, &_defpose); 00124 00125 add_option 00126 ("preload", "", 0, 00127 "Add an <AnimPreload> entry for each animation to the model file(s). " 00128 "This can be used at runtime to support asynchronous " 00129 "loading and binding of animation channels.", 00130 &EggOptchar::dispatch_none, &_preload); 00131 00132 add_option 00133 ("zero", "joint[,hprxyzijkabc]", 0, 00134 "Zeroes out the animation channels for the named joint. If " 00135 "a subset of the component letters hprxyzijkabc is included, the " 00136 "operation is restricted to just those components; otherwise the " 00137 "entire transform is cleared.", 00138 &EggOptchar::dispatch_name_components, NULL, &_zero_channels); 00139 00140 add_option 00141 ("keepall", "", 0, 00142 "Keep all joints and sliders in the character, except those named " 00143 "explicitly by -drop.", 00144 &EggOptchar::dispatch_none, &_keep_all); 00145 00146 add_option 00147 ("p", "joint,parent", 0, 00148 "Moves the named joint under the named parent joint. Use " 00149 "\"-p joint,\" to reparent a joint to the root. The joint transform " 00150 "is recomputed appropriately under its new parent so that the animation " 00151 "is not affected (the effect is similar to NodePath::wrt_reparent_to).", 00152 &EggOptchar::dispatch_vector_string_pair, NULL, &_reparent_joints); 00153 00154 add_option 00155 ("new", "joint,source", 0, 00156 "Creates a new joint under the named parent joint. The new " 00157 "joint will inherit the same net transform as its parent.", 00158 &EggOptchar::dispatch_vector_string_pair, NULL, &_new_joints); 00159 00160 add_option 00161 ("rename", "joint,newjoint", 0, 00162 "Renames the indicated joint, if present, to the given name.", 00163 &EggOptchar::dispatch_vector_string_pair, NULL, &_rename_joints); 00164 00165 if (FFTCompressor::is_compression_available()) { 00166 add_option 00167 ("optimal", "", 0, 00168 "Computes the optimal joint hierarchy for the character by analyzing " 00169 "all of the joint animation and reparenting joints to minimize " 00170 "transformations. This can repair skeletons that have been flattened " 00171 "or whose hierarchy was otherwise damaged in conversion; it can also " 00172 "detect joints that are constrained to follow other joints and should " 00173 "therefore be parented to the master joints. The result is a file " 00174 "from which more joints may be successfully removed, that generally " 00175 "compresses better and with fewer artifacts. However, this is a " 00176 "fairly expensive operation.", 00177 &EggOptchar::dispatch_none, &_optimal_hierarchy); 00178 } 00179 00180 add_option 00181 ("q", "quantum", 0, 00182 "Quantize joint membership values to the given unit. This is " 00183 "the smallest significant change in joint membership. There can " 00184 "be a significant performance (and memory utilization) runtime " 00185 "benefit for eliminating small differences in joint memberships " 00186 "between neighboring vertices. The default is 0.01; specifying " 00187 "0 means to preserve the original values.", 00188 &EggOptchar::dispatch_double, NULL, &_vref_quantum); 00189 00190 add_option 00191 ("qa", "quantum[,hprxyzijkabc]", 0, 00192 "Quantizes animation channels to the given unit. This rounds each " 00193 "of the named components of all joints to the nearest multiple of unit. " 00194 "There is no performance benefit, and little compression benefit, " 00195 "for doing this; and this may introduce visible artifacts to the " 00196 "animation. However, sometimes it is a useful tool for animation " 00197 "analysis and comparison. This option may be repeated several times " 00198 "to quantize different channels by a different amount.", 00199 &EggOptchar::dispatch_double_components, NULL, &_quantize_anims); 00200 00201 add_option 00202 ("dart", "[default, sync, nosync, or structured]", 0, 00203 "change the dart value in the given eggs", 00204 &EggOptchar::dispatch_string, NULL, &_dart_type); 00205 00206 00207 _optimal_hierarchy = false; 00208 _vref_quantum = 0.01; 00209 } 00210 00211 //////////////////////////////////////////////////////////////////// 00212 // Function: EggOptchar::run 00213 // Access: Public 00214 // Description: 00215 //////////////////////////////////////////////////////////////////// 00216 void EggOptchar:: 00217 run() { 00218 // We have to apply the user-specified reparent requests first, 00219 // before we even analyze the joints. This is because reparenting 00220 // the joints may change their properties. 00221 if (apply_user_reparents()) { 00222 nout << "Reparenting hierarchy.\n"; 00223 // So we'll have to call do_reparent() twice. It seems wasteful, 00224 // but it really is necessary, and it's not that bad. 00225 do_reparent(); 00226 } 00227 00228 if (!_zero_channels.empty()) { 00229 zero_channels(); 00230 } 00231 00232 int num_characters = _collection->get_num_characters(); 00233 int ci; 00234 00235 // Now we can analyze the joints for their properties. 00236 for (ci = 0; ci < num_characters; ci++) { 00237 EggCharacterData *char_data = _collection->get_character(ci); 00238 analyze_joints(char_data->get_root_joint(), 0); 00239 analyze_sliders(char_data); 00240 } 00241 00242 if (_list_hierarchy || _list_hierarchy_v) { 00243 for (ci = 0; ci < num_characters; ci++) { 00244 EggCharacterData *char_data = _collection->get_character(ci); 00245 nout << "Character: " << char_data->get_name() << "\n"; 00246 list_joints(char_data->get_root_joint(), 0, _list_hierarchy_v); 00247 list_scalars(char_data, _list_hierarchy_v); 00248 nout << char_data->get_num_joints() << " joints.\n"; 00249 } 00250 00251 } else if (_list_hierarchy_p) { 00252 for (ci = 0; ci < num_characters; ci++) { 00253 EggCharacterData *char_data = _collection->get_character(ci); 00254 nout << "Character: " << char_data->get_name() << "\n"; 00255 int col = 0; 00256 list_joints_p(char_data->get_root_joint(), col); 00257 // A newline to cout is needed after the above call. 00258 cout << "\n"; 00259 nout << char_data->get_num_joints() << " joints.\n"; 00260 } 00261 00262 } else { 00263 // The meat of the program: determine which joints are to be 00264 // removed, and then actually remove them. 00265 determine_removed_components(); 00266 move_vertices(); 00267 if (process_joints()) { 00268 do_reparent(); 00269 } 00270 00271 // We currently do not implement optimizing morph sliders. Need 00272 // to add this at some point; it's quite easy. Identity and empty 00273 // morph sliders can simply be removed, while static sliders need 00274 // to be applied to the vertices and then removed. 00275 00276 rename_joints(); 00277 00278 // Quantize the vertex memberships. We call this even if 00279 // _vref_quantum is 0, because this also normalizes the vertex 00280 // memberships. 00281 quantize_vertices(); 00282 00283 // Also quantize the animation channels, if the user so requested. 00284 quantize_channels(); 00285 00286 // flag all the groups as the user requested. 00287 if (!_flag_groups.empty()) { 00288 Eggs::iterator ei; 00289 for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) { 00290 do_flag_groups(*ei); 00291 } 00292 } 00293 00294 // Add the AnimPreload entries. 00295 if (_preload) { 00296 do_preload(); 00297 } 00298 00299 00300 // Finally, set the default poses. It's important not to do this 00301 // until after we have adjusted all of the transforms for the 00302 // various joints. 00303 if (!_defpose.empty()) { 00304 do_defpose(); 00305 } 00306 00307 if (!_dart_type.empty()) { 00308 Eggs::iterator ei; 00309 for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) { 00310 change_dart_type(*ei, _dart_type); 00311 } 00312 } 00313 00314 write_eggs(); 00315 } 00316 } 00317 00318 //////////////////////////////////////////////////////////////////// 00319 // Function: EggOptchar::handle_args 00320 // Access: Protected, Virtual 00321 // Description: Does something with the additional arguments on the 00322 // command line (after all the -options have been 00323 // parsed). Returns true if the arguments are good, 00324 // false otherwise. 00325 //////////////////////////////////////////////////////////////////// 00326 bool EggOptchar:: 00327 handle_args(ProgramBase::Args &args) { 00328 if (_list_hierarchy || _list_hierarchy_v || _list_hierarchy_p) { 00329 _read_only = true; 00330 } 00331 00332 return EggCharacterFilter::handle_args(args); 00333 } 00334 00335 //////////////////////////////////////////////////////////////////// 00336 // Function: ProgramBase::dispatch_vector_string_pair 00337 // Access: Protected, Static 00338 // Description: Standard dispatch function for an option that takes 00339 // a pair of string parameters. The data pointer is to 00340 // StringPairs vector; the pair will be pushed onto the 00341 // end of the vector. 00342 //////////////////////////////////////////////////////////////////// 00343 bool EggOptchar:: 00344 dispatch_vector_string_pair(const string &opt, const string &arg, void *var) { 00345 StringPairs *ip = (StringPairs *)var; 00346 00347 vector_string words; 00348 tokenize(arg, words, ","); 00349 00350 if (words.size() == 2) { 00351 StringPair sp; 00352 sp._a = words[0]; 00353 sp._b = words[1]; 00354 ip->push_back(sp); 00355 00356 } else { 00357 nout << "-" << opt 00358 << " requires a pair of strings separated by a comma.\n"; 00359 return false; 00360 } 00361 00362 return true; 00363 } 00364 00365 //////////////////////////////////////////////////////////////////// 00366 // Function: ProgramBase::dispatch_name_components 00367 // Access: Protected, Static 00368 // Description: Accepts a name optionally followed by a comma and some 00369 // of the nine standard component letters, 00370 // 00371 // The data pointer is to StringPairs vector; the pair 00372 // will be pushed onto the end of the vector. 00373 //////////////////////////////////////////////////////////////////// 00374 bool EggOptchar:: 00375 dispatch_name_components(const string &opt, const string &arg, void *var) { 00376 StringPairs *ip = (StringPairs *)var; 00377 00378 vector_string words; 00379 tokenize(arg, words, ","); 00380 00381 StringPair sp; 00382 if (words.size() == 1) { 00383 sp._a = words[0]; 00384 00385 } else if (words.size() == 2) { 00386 sp._a = words[0]; 00387 sp._b = words[1]; 00388 00389 } else { 00390 nout << "-" << opt 00391 << " requires a pair of strings separated by a comma.\n"; 00392 return false; 00393 } 00394 00395 if (sp._b.empty()) { 00396 sp._b = matrix_component_letters; 00397 } else { 00398 for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) { 00399 if (strchr(matrix_component_letters, *si) == NULL) { 00400 nout << "Not a standard matrix component: \"" << *si << "\"\n" 00401 << "-" << opt << " requires a joint name followed by a set " 00402 << "of component names. The standard component names are \"" 00403 << matrix_component_letters << "\".\n"; 00404 return false; 00405 } 00406 } 00407 } 00408 00409 ip->push_back(sp); 00410 00411 return true; 00412 } 00413 00414 //////////////////////////////////////////////////////////////////// 00415 // Function: ProgramBase::dispatch_double_components 00416 // Access: Protected, Static 00417 // Description: Accepts a double value optionally followed by a comma 00418 // and some of the nine standard component letters, 00419 // 00420 // The data pointer is to a DoubleStrings vector; the 00421 // pair will be pushed onto the end of the vector. 00422 //////////////////////////////////////////////////////////////////// 00423 bool EggOptchar:: 00424 dispatch_double_components(const string &opt, const string &arg, void *var) { 00425 DoubleStrings *ip = (DoubleStrings *)var; 00426 00427 vector_string words; 00428 tokenize(arg, words, ","); 00429 00430 bool valid_double = false; 00431 00432 DoubleString sp; 00433 if (words.size() == 1) { 00434 valid_double = string_to_double(words[0], sp._a); 00435 00436 } else if (words.size() == 2) { 00437 valid_double = string_to_double(words[0], sp._a); 00438 sp._b = words[1]; 00439 00440 } else { 00441 nout << "-" << opt 00442 << " requires a numeric value followed by a string.\n"; 00443 return false; 00444 } 00445 00446 if (!valid_double) { 00447 nout << "-" << opt 00448 << " requires a numeric value followed by a string.\n"; 00449 return false; 00450 } 00451 00452 if (sp._b.empty()) { 00453 sp._b = matrix_component_letters; 00454 } else { 00455 for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) { 00456 if (strchr(matrix_component_letters, *si) == NULL) { 00457 nout << "Not a standard matrix component: \"" << *si << "\"\n" 00458 << "-" << opt << " requires a joint name followed by a set " 00459 << "of component names. The standard component names are \"" 00460 << matrix_component_letters << "\".\n"; 00461 return false; 00462 } 00463 } 00464 } 00465 00466 ip->push_back(sp); 00467 00468 return true; 00469 } 00470 00471 //////////////////////////////////////////////////////////////////// 00472 // Function: ProgramBase::dispatch_flag_groups 00473 // Access: Protected, Static 00474 // Description: Accepts a set of comma-delimited group names followed 00475 // by an optional name separated with an equal sign. 00476 // 00477 // The data pointer is to a FlagGroups object. 00478 //////////////////////////////////////////////////////////////////// 00479 bool EggOptchar:: 00480 dispatch_flag_groups(const string &opt, const string &arg, void *var) { 00481 FlagGroups *ip = (FlagGroups *)var; 00482 00483 vector_string words; 00484 00485 tokenize(arg, words, ","); 00486 00487 if (words.empty()) { 00488 nout << "-" << opt 00489 << " requires a series of words separated by a comma.\n"; 00490 return false; 00491 } 00492 00493 FlagGroupsEntry entry; 00494 00495 // Check for an equal sign in the last word. This marks the name to 00496 // assign. 00497 string &last_word = words.back(); 00498 size_t equals = last_word.rfind('='); 00499 if (equals != string::npos) { 00500 entry._name = last_word.substr(equals + 1); 00501 last_word = last_word.substr(0, equals); 00502 00503 } else { 00504 // If there's no equal sign, the default is to name all groups 00505 // after the group itself. We leave the name empty to indicate 00506 // that. 00507 } 00508 00509 // Convert the words to GlobPatterns. 00510 vector_string::const_iterator si; 00511 for (si = words.begin(); si != words.end(); ++si) { 00512 const string &word = (*si); 00513 entry._groups.push_back(GlobPattern(word)); 00514 } 00515 00516 ip->push_back(entry); 00517 00518 return true; 00519 } 00520 00521 //////////////////////////////////////////////////////////////////// 00522 // Function: EggOptchar::determine_removed_components 00523 // Access: Private 00524 // Description: Flag all joints and sliders that should be removed 00525 // for optimization purposes. 00526 //////////////////////////////////////////////////////////////////// 00527 void EggOptchar:: 00528 determine_removed_components() { 00529 typedef pset<string> Names; 00530 Names keep_names; 00531 Names drop_names; 00532 Names expose_names; 00533 Names suppress_names; 00534 Names names_used; 00535 00536 vector_string::const_iterator si; 00537 for (si = _keep_components.begin(); si != _keep_components.end(); ++si) { 00538 keep_names.insert(*si); 00539 } 00540 for (si = _drop_components.begin(); si != _drop_components.end(); ++si) { 00541 drop_names.insert(*si); 00542 } 00543 for (si = _expose_components.begin(); si != _expose_components.end(); ++si) { 00544 keep_names.insert(*si); 00545 expose_names.insert(*si); 00546 } 00547 for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) { 00548 suppress_names.insert(*si); 00549 } 00550 00551 // We always keep the root joint, which has no name. 00552 keep_names.insert(""); 00553 00554 int num_characters = _collection->get_num_characters(); 00555 for (int ci = 0; ci < num_characters; ci++) { 00556 EggCharacterData *char_data = _collection->get_character(ci); 00557 int num_components = char_data->get_num_components(); 00558 nout << char_data->get_name() << " has " << num_components << " components.\n"; 00559 for (int i = 0; i < num_components; i++) { 00560 EggComponentData *comp_data = char_data->get_component(i); 00561 nassertv(comp_data != (EggComponentData *)NULL); 00562 00563 EggOptcharUserData *user_data = 00564 DCAST(EggOptcharUserData, comp_data->get_user_data()); 00565 nassertv(user_data != (EggOptcharUserData *)NULL); 00566 00567 const string &name = comp_data->get_name(); 00568 if (suppress_names.find(name) != suppress_names.end()) { 00569 // If this component is not dropped, it will not be implicitly 00570 // exposed. 00571 names_used.insert(name); 00572 user_data->_flags |= EggOptcharUserData::F_suppress; 00573 } 00574 00575 if (drop_names.find(name) != drop_names.end()) { 00576 // Remove this component by user request. 00577 names_used.insert(name); 00578 user_data->_flags |= EggOptcharUserData::F_remove; 00579 00580 } else if (_keep_all || keep_names.find(name) != keep_names.end()) { 00581 // Keep this component. 00582 names_used.insert(name); 00583 00584 if (expose_names.find(name) != expose_names.end()) { 00585 // In fact, expose it. 00586 user_data->_flags |= EggOptcharUserData::F_expose; 00587 } 00588 00589 } else { 00590 // Remove this component if it's unanimated or empty. 00591 if ((user_data->_flags & (EggOptcharUserData::F_static | EggOptcharUserData::F_empty)) != 0) { 00592 if ((user_data->_flags & (EggOptcharUserData::F_top | EggOptcharUserData::F_empty)) == EggOptcharUserData::F_top) { 00593 // Actually, we can't remove it if it's a top joint, 00594 // unless it's also empty. That's because vertices that 00595 // are partially assigned to this joint would then have no 00596 // joint to represent the same partial assignment, and 00597 // they would then appear to be wholly assigned to their 00598 // other joint, which would be incorrect. 00599 00600 } else { 00601 // But joints that aren't top joints (or that are empty) 00602 // are o.k. to remove. 00603 user_data->_flags |= EggOptcharUserData::F_remove; 00604 } 00605 } 00606 } 00607 } 00608 } 00609 00610 // Go back and tell the user about component names we didn't use, 00611 // just to be helpful. 00612 for (si = _keep_components.begin(); si != _keep_components.end(); ++si) { 00613 const string &name = (*si); 00614 if (names_used.find(name) == names_used.end()) { 00615 nout << "No such component: " << name << "\n"; 00616 } 00617 } 00618 for (si = _drop_components.begin(); si != _drop_components.end(); ++si) { 00619 const string &name = (*si); 00620 if (names_used.find(name) == names_used.end()) { 00621 nout << "No such component: " << name << "\n"; 00622 } 00623 } 00624 for (si = _expose_components.begin(); si != _expose_components.end(); ++si) { 00625 const string &name = (*si); 00626 if (names_used.find(name) == names_used.end()) { 00627 nout << "No such component: " << name << "\n"; 00628 } 00629 } 00630 for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) { 00631 const string &name = (*si); 00632 if (names_used.find(name) == names_used.end()) { 00633 nout << "No such component: " << name << "\n"; 00634 } 00635 } 00636 } 00637 00638 //////////////////////////////////////////////////////////////////// 00639 // Function: EggOptchar::move_vertices 00640 // Access: Private 00641 // Description: Moves the vertices from joints that are about to be 00642 // removed into the first suitable parent. This might 00643 // result in fewer joints being removed (because 00644 // the parent might suddenly no longer be empty). 00645 //////////////////////////////////////////////////////////////////// 00646 void EggOptchar:: 00647 move_vertices() { 00648 int num_characters = _collection->get_num_characters(); 00649 for (int ci = 0; ci < num_characters; ci++) { 00650 EggCharacterData *char_data = _collection->get_character(ci); 00651 int num_joints = char_data->get_num_joints(); 00652 00653 for (int i = 0; i < num_joints; i++) { 00654 EggJointData *joint_data = char_data->get_joint(i); 00655 EggOptcharUserData *user_data = 00656 DCAST(EggOptcharUserData, joint_data->get_user_data()); 00657 00658 if ((user_data->_flags & EggOptcharUserData::F_empty) == 0 && 00659 (user_data->_flags & EggOptcharUserData::F_remove) != 0) { 00660 // This joint has vertices, but is scheduled to be removed; 00661 // find a suitable home for its vertices. 00662 EggJointData *best_joint = find_best_vertex_joint(joint_data->get_parent()); 00663 joint_data->move_vertices_to(best_joint); 00664 00665 // Now we can't remove the joint. 00666 if (best_joint != (EggJointData *)NULL) { 00667 EggOptcharUserData *best_user_data = 00668 DCAST(EggOptcharUserData, best_joint->get_user_data()); 00669 best_user_data->_flags &= ~(EggOptcharUserData::F_empty | EggOptcharUserData::F_remove); 00670 } 00671 } 00672 } 00673 } 00674 } 00675 00676 00677 //////////////////////////////////////////////////////////////////// 00678 // Function: EggOptchar::process_joints 00679 // Access: Private 00680 // Description: Effects the actual removal of joints flagged for 00681 // removal by reparenting the hierarchy appropriately. 00682 // Returns true if any joints are removed, false 00683 // otherwise. 00684 //////////////////////////////////////////////////////////////////// 00685 bool EggOptchar:: 00686 process_joints() { 00687 bool removed_any = false; 00688 int num_characters = _collection->get_num_characters(); 00689 for (int ci = 0; ci < num_characters; ci++) { 00690 EggCharacterData *char_data = _collection->get_character(ci); 00691 int num_joints = char_data->get_num_joints(); 00692 00693 int num_static = 0; 00694 int num_empty = 0; 00695 int num_identity = 0; 00696 int num_other = 0; 00697 int num_kept = 0; 00698 00699 for (int i = 0; i < num_joints; i++) { 00700 EggJointData *joint_data = char_data->get_joint(i); 00701 EggOptcharUserData *user_data = 00702 DCAST(EggOptcharUserData, joint_data->get_user_data()); 00703 00704 if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) { 00705 // This joint will be removed, so reparent it to nothing. 00706 joint_data->reparent_to((EggJointData *)NULL); 00707 00708 // Determine what kind of node it is we're removing, for the 00709 // user's information. 00710 if ((user_data->_flags & EggOptcharUserData::F_identity) != 0) { 00711 num_identity++; 00712 } else if ((user_data->_flags & EggOptcharUserData::F_static) != 0) { 00713 num_static++; 00714 } else if ((user_data->_flags & EggOptcharUserData::F_empty) != 0) { 00715 num_empty++; 00716 } else { 00717 num_other++; 00718 } 00719 removed_any = true; 00720 00721 } else { 00722 // This joint will be preserved, but maybe its parent will 00723 // change. 00724 EggJointData *best_parent = find_best_parent(joint_data->get_parent()); 00725 joint_data->reparent_to(best_parent); 00726 if ((user_data->_flags & EggOptcharUserData::F_expose) != 0) { 00727 joint_data->expose(); 00728 } else if ((user_data->_flags & EggOptcharUserData::F_suppress) != 0) { 00729 joint_data->expose(EggGroup::DC_none); 00730 } 00731 num_kept++; 00732 } 00733 } 00734 00735 if (num_joints == num_kept) { 00736 nout << char_data->get_name() << ": keeping " << num_joints 00737 << " joints.\n"; 00738 } else { 00739 nout << setw(5) << num_joints 00740 << " original joints in " << char_data->get_name() 00741 << "\n"; 00742 if (num_identity != 0) { 00743 nout << setw(5) << num_identity << " identity joints\n"; 00744 } 00745 if (num_static != 0) { 00746 nout << setw(5) << num_static << " unanimated joints\n"; 00747 } 00748 if (num_empty != 0) { 00749 nout << setw(5) << num_empty << " empty joints\n"; 00750 } 00751 if (num_other != 0) { 00752 nout << setw(5) << num_other << " other joints\n"; 00753 } 00754 nout << " ----\n" 00755 << setw(5) << num_kept << " joints remaining\n\n"; 00756 } 00757 } 00758 00759 return removed_any; 00760 } 00761 00762 //////////////////////////////////////////////////////////////////// 00763 // Function: EggOptchar::find_best_parent 00764 // Access: Private 00765 // Description: Searches for the first joint at this level or above 00766 // that is not scheduled to be removed. This is the 00767 // joint that the first child of this joint should be 00768 // reparented to. 00769 //////////////////////////////////////////////////////////////////// 00770 EggJointData *EggOptchar:: 00771 find_best_parent(EggJointData *joint_data) const { 00772 EggOptcharUserData *user_data = 00773 DCAST(EggOptcharUserData, joint_data->get_user_data()); 00774 00775 if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) { 00776 // Keep going. 00777 if (joint_data->get_parent() != (EggJointData *)NULL) { 00778 return find_best_parent(joint_data->get_parent()); 00779 } 00780 } 00781 00782 // This is the one! 00783 return joint_data; 00784 } 00785 00786 //////////////////////////////////////////////////////////////////// 00787 // Function: EggOptchar::find_best_vertex_joint 00788 // Access: Private 00789 // Description: Searches for the first joint at this level or above 00790 // that is not static. This is the joint that the 00791 // vertices of this joint should be moved into. 00792 //////////////////////////////////////////////////////////////////// 00793 EggJointData *EggOptchar:: 00794 find_best_vertex_joint(EggJointData *joint_data) const { 00795 if (joint_data == (EggJointData *)NULL) { 00796 return NULL; 00797 } 00798 00799 EggOptcharUserData *user_data = 00800 DCAST(EggOptcharUserData, joint_data->get_user_data()); 00801 00802 if ((user_data->_flags & EggOptcharUserData::F_static) != 0) { 00803 // Keep going. 00804 return find_best_vertex_joint(joint_data->get_parent()); 00805 } 00806 00807 // This is the one! 00808 return joint_data; 00809 } 00810 00811 //////////////////////////////////////////////////////////////////// 00812 // Function: EggOptchar::apply_user_reparents 00813 // Access: Private 00814 // Description: Reparents all the joints that the user suggested on 00815 // the command line. Returns true if any operations 00816 // were performed, false otherwise. 00817 //////////////////////////////////////////////////////////////////// 00818 bool EggOptchar:: 00819 apply_user_reparents() { 00820 bool did_anything = false; 00821 00822 int num_characters = _collection->get_num_characters(); 00823 00824 // First, get the new joints. 00825 StringPairs::const_iterator spi; 00826 for (spi = _new_joints.begin(); spi != _new_joints.end(); ++spi) { 00827 const StringPair &p = (*spi); 00828 00829 for (int ci = 0; ci < num_characters; ci++) { 00830 EggCharacterData *char_data = _collection->get_character(ci); 00831 EggJointData *node_a = char_data->find_joint(p._a); 00832 EggJointData *node_b = char_data->get_root_joint(); 00833 if (!p._b.empty()) { 00834 node_b = char_data->find_joint(p._b); 00835 } 00836 00837 if (node_b == (EggJointData *)NULL) { 00838 nout << "No joint named " << p._b << " in " << char_data->get_name() 00839 << ".\n"; 00840 00841 } else if (node_a != (EggJointData *)NULL) { 00842 nout << "Joint " << p._a << " already exists in " 00843 << char_data->get_name() << ".\n"; 00844 00845 } else { 00846 nout << "Creating new joint " << p._a << " in " 00847 << char_data->get_name() << ".\n"; 00848 node_a = char_data->make_new_joint(p._a, node_b); 00849 did_anything = true; 00850 } 00851 } 00852 } 00853 00854 // Now get the user reparents. 00855 for (spi = _reparent_joints.begin(); spi != _reparent_joints.end(); ++spi) { 00856 const StringPair &p = (*spi); 00857 00858 for (int ci = 0; ci < num_characters; ci++) { 00859 EggCharacterData *char_data = _collection->get_character(ci); 00860 EggJointData *node_a = char_data->find_joint(p._a); 00861 EggJointData *node_b = char_data->get_root_joint(); 00862 if (!p._b.empty()) { 00863 node_b = char_data->find_joint(p._b); 00864 } 00865 00866 if (node_b == (EggJointData *)NULL) { 00867 nout << "No joint named " << p._b << " in " << char_data->get_name() 00868 << ".\n"; 00869 } else if (node_a == (EggJointData *)NULL) { 00870 nout << "No joint named " << p._a << " in " << char_data->get_name() 00871 << ".\n"; 00872 } else { 00873 node_a->reparent_to(node_b); 00874 did_anything = true; 00875 } 00876 } 00877 } 00878 00879 if (_optimal_hierarchy) { 00880 did_anything = true; 00881 for (int ci = 0; ci < num_characters; ci++) { 00882 EggCharacterData *char_data = _collection->get_character(ci); 00883 nout << "Computing optimal hierarchy for " 00884 << char_data->get_name() << ".\n"; 00885 char_data->choose_optimal_hierarchy(); 00886 nout << "Done computing optimal hierarchy for " 00887 << char_data->get_name() << ".\n"; 00888 } 00889 } 00890 00891 return did_anything; 00892 } 00893 00894 //////////////////////////////////////////////////////////////////// 00895 // Function: EggOptchar::zero_channels 00896 // Access: Private 00897 // Description: Zeroes out the channels specified by the user on the 00898 // command line. 00899 // 00900 // Returns true if any operation was performed, false 00901 // otherwise. 00902 //////////////////////////////////////////////////////////////////// 00903 bool EggOptchar:: 00904 zero_channels() { 00905 bool did_anything = false; 00906 int num_characters = _collection->get_num_characters(); 00907 00908 StringPairs::const_iterator spi; 00909 for (spi = _zero_channels.begin(); spi != _zero_channels.end(); ++spi) { 00910 const StringPair &p = (*spi); 00911 00912 for (int ci = 0; ci < num_characters; ci++) { 00913 EggCharacterData *char_data = _collection->get_character(ci); 00914 EggJointData *joint_data = char_data->find_joint(p._a); 00915 00916 if (joint_data == (EggJointData *)NULL) { 00917 nout << "No joint named " << p._a << " in " << char_data->get_name() 00918 << ".\n"; 00919 } else { 00920 joint_data->zero_channels(p._b); 00921 did_anything = true; 00922 } 00923 } 00924 } 00925 00926 return did_anything; 00927 } 00928 00929 //////////////////////////////////////////////////////////////////// 00930 // Function: EggOptchar::quantize_channels 00931 // Access: Private 00932 // Description: Quantizes the channels specified by the user on the 00933 // command line. 00934 // 00935 // Returns true if any operation was performed, false 00936 // otherwise. 00937 //////////////////////////////////////////////////////////////////// 00938 bool EggOptchar:: 00939 quantize_channels() { 00940 bool did_anything = false; 00941 int num_characters = _collection->get_num_characters(); 00942 00943 DoubleStrings::const_iterator spi; 00944 for (spi = _quantize_anims.begin(); spi != _quantize_anims.end(); ++spi) { 00945 const DoubleString &p = (*spi); 00946 00947 for (int ci = 0; ci < num_characters; ci++) { 00948 EggCharacterData *char_data = _collection->get_character(ci); 00949 EggJointData *joint_data = char_data->get_root_joint(); 00950 00951 if (joint_data != (EggJointData *)NULL) { 00952 joint_data->quantize_channels(p._b, p._a); 00953 did_anything = true; 00954 } 00955 } 00956 } 00957 00958 return did_anything; 00959 } 00960 00961 //////////////////////////////////////////////////////////////////// 00962 // Function: EggOptchar::analyze_joints 00963 // Access: Private 00964 // Description: Recursively walks the joint hierarchy for a 00965 // particular character, indentifying properties of each 00966 // joint. 00967 //////////////////////////////////////////////////////////////////// 00968 void EggOptchar:: 00969 analyze_joints(EggJointData *joint_data, int level) { 00970 PT(EggOptcharUserData) user_data = new EggOptcharUserData; 00971 joint_data->set_user_data(user_data); 00972 00973 if (level == 1) { 00974 // The child joints of the root joint are deemed "top" joints. 00975 // These may not be removed unless they are empty (because their 00976 // vertices have no joint to be moved into). 00977 user_data->_flags |= EggOptcharUserData::F_top; 00978 } 00979 00980 // Analyze the table of matrices for this joint, checking to see if 00981 // they're all the same across all frames, or if any of them are 00982 // different; also look for empty joints (that control no vertices). 00983 int num_mats = 0; 00984 bool different_mat = false; 00985 bool has_vertices = false; 00986 00987 int num_models = joint_data->get_num_models(); 00988 int i; 00989 for (i = 0; i < num_models; i++) { 00990 if (joint_data->has_model(i)) { 00991 EggBackPointer *model = joint_data->get_model(i); 00992 if (model->has_vertices()) { 00993 has_vertices = true; 00994 } 00995 00996 int num_frames = joint_data->get_num_frames(i); 00997 00998 int f; 00999 for (f = 0; f < num_frames && !different_mat; f++) { 01000 LMatrix4d mat = joint_data->get_frame(i, f); 01001 num_mats++; 01002 if (num_mats == 1) { 01003 // This is the first matrix. 01004 user_data->_static_mat = mat; 01005 01006 } else { 01007 // This is a second or later matrix. 01008 if (!mat.almost_equal(user_data->_static_mat, 0.0001)) { 01009 // It's different than the first one. 01010 different_mat = true; 01011 } 01012 } 01013 } 01014 } 01015 } 01016 01017 if (!different_mat) { 01018 // All the mats are the same for this joint. 01019 user_data->_flags |= EggOptcharUserData::F_static; 01020 01021 if (num_mats == 0 || 01022 user_data->_static_mat.almost_equal(LMatrix4d::ident_mat(), 0.0001)) { 01023 // It's not only static, but it's the identity matrix. 01024 user_data->_flags |= EggOptcharUserData::F_identity; 01025 } 01026 } 01027 01028 if (!has_vertices) { 01029 // There are no vertices in this joint. 01030 user_data->_flags |= EggOptcharUserData::F_empty; 01031 } 01032 01033 int num_children = joint_data->get_num_children(); 01034 for (i = 0; i < num_children; i++) { 01035 analyze_joints(joint_data->get_child(i), level + 1); 01036 } 01037 } 01038 01039 //////////////////////////////////////////////////////////////////// 01040 // Function: EggOptchar::analyze_sliders 01041 // Access: Private 01042 // Description: Linearly walks the slider list for a particular 01043 // character, indentifying properties of each slider. 01044 //////////////////////////////////////////////////////////////////// 01045 void EggOptchar:: 01046 analyze_sliders(EggCharacterData *char_data) { 01047 int num_sliders = char_data->get_num_sliders(); 01048 for (int si = 0; si < num_sliders; si++) { 01049 EggSliderData *slider_data = char_data->get_slider(si); 01050 01051 PT(EggOptcharUserData) user_data = new EggOptcharUserData; 01052 slider_data->set_user_data(user_data); 01053 01054 // Analyze the table of values for this slider, checking to see if 01055 // they're all the same across all frames, or if any of them are 01056 // different; also look for empty sliders (that control no vertices). 01057 int num_values = 0; 01058 bool different_value = false; 01059 bool has_vertices = false; 01060 01061 int num_models = slider_data->get_num_models(); 01062 for (int i = 0; i < num_models; i++) { 01063 if (slider_data->has_model(i)) { 01064 EggBackPointer *model = slider_data->get_model(i); 01065 if (model->has_vertices()) { 01066 has_vertices = true; 01067 } 01068 01069 int num_frames = slider_data->get_num_frames(i); 01070 01071 int f; 01072 for (f = 0; f < num_frames && !different_value; f++) { 01073 double value = slider_data->get_frame(i, f); 01074 num_values++; 01075 if (num_values == 1) { 01076 // This is the first value. 01077 user_data->_static_value = value; 01078 01079 } else { 01080 // This is a second or later value. 01081 if (!IS_THRESHOLD_EQUAL(value, user_data->_static_value, 0.0001)) { 01082 // It's different than the first one. 01083 different_value = true; 01084 } 01085 } 01086 } 01087 } 01088 } 01089 01090 if (!different_value) { 01091 // All the values are the same for this slider. 01092 user_data->_flags |= EggOptcharUserData::F_static; 01093 01094 if (num_values == 0 || IS_THRESHOLD_ZERO(user_data->_static_value, 0.0001)) { 01095 // It's not only static, but it's the identity value. 01096 user_data->_flags |= EggOptcharUserData::F_identity; 01097 } 01098 } 01099 01100 if (!has_vertices) { 01101 // There are no vertices in this slider. 01102 user_data->_flags |= EggOptcharUserData::F_empty; 01103 } 01104 } 01105 } 01106 01107 //////////////////////////////////////////////////////////////////// 01108 // Function: EggOptchar::list_joints 01109 // Access: Private 01110 // Description: Outputs a list of the joint hierarchy. 01111 //////////////////////////////////////////////////////////////////// 01112 void EggOptchar:: 01113 list_joints(EggJointData *joint_data, int indent_level, bool verbose) { 01114 // Don't list the root joint, which is artificially created when the 01115 // character is loaded. Instead, list each child as it is 01116 // encountered. 01117 01118 int num_children = joint_data->get_num_children(); 01119 for (int i = 0; i < num_children; i++) { 01120 EggJointData *child_data = joint_data->get_child(i); 01121 describe_component(child_data, indent_level, verbose); 01122 01123 list_joints(child_data, indent_level + 2, verbose); 01124 } 01125 } 01126 01127 //////////////////////////////////////////////////////////////////// 01128 // Function: EggOptchar::list_joints_p 01129 // Access: Private 01130 // Description: Outputs a list of the joint hierarchy as a series of 01131 // -p joint,parent commands. 01132 //////////////////////////////////////////////////////////////////// 01133 void EggOptchar:: 01134 list_joints_p(EggJointData *joint_data, int &col) { 01135 // As above, don't list the root joint. 01136 01137 int num_children = joint_data->get_num_children(); 01138 static const int max_col = 72; 01139 01140 for (int i = 0; i < num_children; i++) { 01141 EggJointData *child_data = joint_data->get_child(i); 01142 // We send output to cout instead of nout to avoid the 01143 // word-wrapping, and also to allow the user to redirect this 01144 // easily to a file. 01145 01146 string text = string(" -p ") + child_data->get_name() + 01147 string(",") + joint_data->get_name(); 01148 if (col == 0) { 01149 cout << " " << text; 01150 col = 4 + text.length(); 01151 } else { 01152 col += text.length(); 01153 if (col >= max_col) { 01154 cout << " \\\n " << text; 01155 col = 4 + text.length(); 01156 } else { 01157 cout << text; 01158 } 01159 } 01160 01161 list_joints_p(child_data, col); 01162 } 01163 } 01164 01165 //////////////////////////////////////////////////////////////////// 01166 // Function: EggOptchar::list_scalars 01167 // Access: Private 01168 // Description: Outputs a list of the scalars. 01169 //////////////////////////////////////////////////////////////////// 01170 void EggOptchar:: 01171 list_scalars(EggCharacterData *char_data, bool verbose) { 01172 int num_sliders = char_data->get_num_sliders(); 01173 for (int si = 0; si < num_sliders; si++) { 01174 EggSliderData *slider_data = char_data->get_slider(si); 01175 describe_component(slider_data, 0, verbose); 01176 } 01177 } 01178 01179 //////////////////////////////////////////////////////////////////// 01180 // Function: EggOptchar::describe_component 01181 // Access: Private 01182 // Description: Describes one particular slider or joint. 01183 //////////////////////////////////////////////////////////////////// 01184 void EggOptchar:: 01185 describe_component(EggComponentData *comp_data, int indent_level, 01186 bool verbose) { 01187 // We use cout instead of nout so the user can easily redirect this 01188 // to a file. 01189 indent(cout, indent_level) 01190 << comp_data->get_name(); 01191 01192 if (verbose) { 01193 EggOptcharUserData *user_data = 01194 DCAST(EggOptcharUserData, comp_data->get_user_data()); 01195 if (user_data->is_identity()) { 01196 cout << " (identity)"; 01197 } else if (user_data->is_static()) { 01198 cout << " (static)"; 01199 } 01200 if (user_data->is_empty()) { 01201 cout << " (empty)"; 01202 } 01203 if (user_data->is_top()) { 01204 cout << " (top)"; 01205 } 01206 } 01207 cout << "\n"; 01208 } 01209 01210 //////////////////////////////////////////////////////////////////// 01211 // Function: EggOptchar::do_reparent 01212 // Access: Private 01213 // Description: Performs all of the queued up reparenting operations. 01214 //////////////////////////////////////////////////////////////////// 01215 void EggOptchar:: 01216 do_reparent() { 01217 bool all_ok = true; 01218 01219 int num_characters = _collection->get_num_characters(); 01220 for (int ci = 0; ci < num_characters; ci++) { 01221 EggCharacterData *char_data = _collection->get_character(ci); 01222 if (!char_data->do_reparent()) { 01223 all_ok = false; 01224 } 01225 } 01226 01227 if (!all_ok) { 01228 exit(1); 01229 } 01230 } 01231 01232 //////////////////////////////////////////////////////////////////// 01233 // Function: EggOptchar::quantize_vertices 01234 // Access: Private 01235 // Description: Walks through all of the loaded egg files, looking 01236 // for vertices whose joint memberships are then 01237 // quantized according to _vref_quantum. 01238 //////////////////////////////////////////////////////////////////// 01239 void EggOptchar:: 01240 quantize_vertices() { 01241 Eggs::iterator ei; 01242 for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) { 01243 quantize_vertices(*ei); 01244 } 01245 } 01246 01247 //////////////////////////////////////////////////////////////////// 01248 // Function: EggOptchar::quantize_vertices 01249 // Access: Private 01250 // Description: Recursively walks through the indicated egg 01251 // hierarchy, looking for vertices whose joint 01252 // memberships are then quantized according to 01253 // _vref_quantum. 01254 //////////////////////////////////////////////////////////////////// 01255 void EggOptchar:: 01256 quantize_vertices(EggNode *egg_node) { 01257 if (egg_node->is_of_type(EggVertexPool::get_class_type())) { 01258 EggVertexPool *vpool = DCAST(EggVertexPool, egg_node); 01259 EggVertexPool::iterator vi; 01260 for (vi = vpool->begin(); vi != vpool->end(); ++vi) { 01261 quantize_vertex(*vi); 01262 } 01263 01264 } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { 01265 EggGroupNode *group = DCAST(EggGroupNode, egg_node); 01266 EggGroupNode::iterator ci; 01267 for (ci = group->begin(); ci != group->end(); ++ci) { 01268 quantize_vertices(*ci); 01269 } 01270 } 01271 } 01272 01273 //////////////////////////////////////////////////////////////////// 01274 // Function: EggOptchar::quantize_vertex 01275 // Access: Private 01276 // Description: Quantizes the indicated vertex's joint membership. 01277 //////////////////////////////////////////////////////////////////// 01278 void EggOptchar:: 01279 quantize_vertex(EggVertex *egg_vertex) { 01280 if (egg_vertex->gref_size() == 0) { 01281 // Never mind on this vertex. 01282 return; 01283 } 01284 01285 // First, get a copy of the existing membership. 01286 VertexMemberships memberships; 01287 EggVertex::GroupRef::const_iterator gi; 01288 double net_membership = 0.0; 01289 for (gi = egg_vertex->gref_begin(); gi != egg_vertex->gref_end(); ++gi) { 01290 EggGroup *group = (*gi); 01291 double membership = group->get_vertex_membership(egg_vertex); 01292 memberships.push_back(VertexMembership(group, membership)); 01293 net_membership += membership; 01294 } 01295 nassertv(net_membership != 0.0); 01296 01297 // Now normalize all the memberships so the net membership is 1.0, 01298 // and then quantize the result (if the user so requested). 01299 double factor = 1.0 / net_membership; 01300 net_membership = 0.0; 01301 VertexMemberships::iterator mi; 01302 VertexMemberships::iterator largest = memberships.begin(); 01303 01304 for (mi = memberships.begin(); mi != memberships.end(); ++mi) { 01305 if ((*largest) < (*mi)) { 01306 // Remember the largest membership value, so we can readjust it 01307 // at the end. 01308 largest = mi; 01309 } 01310 01311 double value = (*mi)._membership * factor; 01312 if (_vref_quantum != 0.0) { 01313 value = floor(value / _vref_quantum + 0.5) * _vref_quantum; 01314 } 01315 (*mi)._membership = value; 01316 01317 net_membership += value; 01318 } 01319 01320 // The the largest membership value gets corrected again by the 01321 // roundoff error. 01322 (*largest)._membership += 1.0 - net_membership; 01323 01324 // Finally, walk back through and apply these computed values to the 01325 // vertex. 01326 for (mi = memberships.begin(); mi != memberships.end(); ++mi) { 01327 (*mi)._group->set_vertex_membership(egg_vertex, (*mi)._membership); 01328 } 01329 } 01330 01331 //////////////////////////////////////////////////////////////////// 01332 // Function: EggOptchar::do_flag_groups 01333 // Access: Private 01334 // Description: Recursively walks the indicated egg hierarchy, 01335 // looking for groups that match one of the group names 01336 // in _flag_groups, and renaming geometry appropriately. 01337 //////////////////////////////////////////////////////////////////// 01338 void EggOptchar:: 01339 do_flag_groups(EggGroupNode *egg_group) { 01340 bool matched = false; 01341 string name; 01342 FlagGroups::const_iterator fi; 01343 for (fi = _flag_groups.begin(); 01344 fi != _flag_groups.end() && !matched; 01345 ++fi) { 01346 const FlagGroupsEntry &entry = (*fi); 01347 Globs::const_iterator si; 01348 for (si = entry._groups.begin(); 01349 si != entry._groups.end() && !matched; 01350 ++si) { 01351 if ((*si).matches(egg_group->get_name())) { 01352 matched = true; 01353 if (!entry._name.empty()) { 01354 name = entry._name; 01355 } else { 01356 name = egg_group->get_name(); 01357 } 01358 } 01359 } 01360 } 01361 01362 if (matched) { 01363 // Ok, this group matched one of the user's command-line renames. 01364 // Rename all the primitives in this group and below to the 01365 // indicated name; this will expose the primitives through the 01366 // character loader. 01367 rename_primitives(egg_group, name); 01368 } 01369 01370 // Now recurse on children. 01371 EggGroupNode::iterator gi; 01372 for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) { 01373 EggNode *child = (*gi); 01374 if (child->is_of_type(EggGroupNode::get_class_type())) { 01375 EggGroupNode *group = DCAST(EggGroupNode, child); 01376 do_flag_groups(group); 01377 } 01378 } 01379 } 01380 01381 //////////////////////////////////////////////////////////////////// 01382 // Function: EggOptchar::rename_joints 01383 // Access: Private 01384 // Description: Rename all the joints named with the -rename 01385 // command-line option. 01386 //////////////////////////////////////////////////////////////////// 01387 void EggOptchar:: 01388 rename_joints() { 01389 for (StringPairs::iterator spi = _rename_joints.begin(); 01390 spi != _rename_joints.end(); 01391 ++spi) { 01392 const StringPair &sp = (*spi); 01393 int num_characters = _collection->get_num_characters(); 01394 int ci; 01395 for (ci = 0; ci < num_characters; ++ci) { 01396 EggCharacterData *char_data = _collection->get_character(ci); 01397 EggJointData *joint = char_data->find_joint(sp._a); 01398 if (joint != (EggJointData *)NULL) { 01399 nout << "Renaming joint " << sp._a << " to " << sp._b << "\n"; 01400 joint->set_name(sp._b); 01401 01402 int num_models = joint->get_num_models(); 01403 for (int mn = 0; mn < num_models; ++mn) { 01404 if (joint->has_model(mn)) { 01405 EggBackPointer *model = joint->get_model(mn); 01406 model->set_name(sp._b); 01407 } 01408 } 01409 01410 } else { 01411 nout << "Couldn't find joint " << sp._a << "\n"; 01412 } 01413 } 01414 } 01415 } 01416 01417 //////////////////////////////////////////////////////////////////// 01418 // Function: EggOptchar::change_dart_type 01419 // Access: Private 01420 // Description: Recursively walks the indicated egg hierarchy, 01421 // renaming geometry to the indicated name. 01422 //////////////////////////////////////////////////////////////////// 01423 void EggOptchar:: 01424 change_dart_type(EggGroupNode *egg_group, const string &new_dart_type) { 01425 EggGroupNode::iterator gi; 01426 for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) { 01427 EggNode *child = (*gi); 01428 if (child->is_of_type(EggGroupNode::get_class_type())) { 01429 EggGroupNode *group = DCAST(EggGroupNode, child); 01430 if (child->is_of_type(EggGroup::get_class_type())) { 01431 EggGroup *gr = DCAST(EggGroup, child); 01432 EggGroup::DartType dt = gr->get_dart_type(); 01433 if(dt != EggGroup::DT_none) { 01434 EggGroup::DartType newDt = gr->string_dart_type(new_dart_type); 01435 gr->set_dart_type(newDt); 01436 } 01437 } 01438 change_dart_type(group, new_dart_type); 01439 } 01440 } 01441 } 01442 01443 01444 //////////////////////////////////////////////////////////////////// 01445 // Function: EggOptchar::rename_primitives 01446 // Access: Private 01447 // Description: Recursively walks the indicated egg hierarchy, 01448 // renaming geometry to the indicated name. 01449 //////////////////////////////////////////////////////////////////// 01450 void EggOptchar:: 01451 rename_primitives(EggGroupNode *egg_group, const string &name) { 01452 EggGroupNode::iterator gi; 01453 for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) { 01454 EggNode *child = (*gi); 01455 01456 if (child->is_of_type(EggGroupNode::get_class_type())) { 01457 EggGroupNode *group = DCAST(EggGroupNode, child); 01458 rename_primitives(group, name); 01459 01460 } else if (child->is_of_type(EggPrimitive::get_class_type())) { 01461 child->set_name(name); 01462 } 01463 } 01464 } 01465 01466 //////////////////////////////////////////////////////////////////// 01467 // Function: EggOptchar::do_preload 01468 // Access: Private 01469 // Description: Generates the preload tables for each model. 01470 //////////////////////////////////////////////////////////////////// 01471 void EggOptchar:: 01472 do_preload() { 01473 // First, build up the list of AnimPreload entries, one for each 01474 // animation file. 01475 PT(EggGroup) anim_group = new EggGroup("preload"); 01476 01477 int num_characters = _collection->get_num_characters(); 01478 int ci; 01479 for (ci = 0; ci < num_characters; ++ci) { 01480 EggCharacterData *char_data = _collection->get_character(ci); 01481 01482 int num_models = char_data->get_num_models(); 01483 for (int mn = 0; mn < num_models; ++mn) { 01484 EggNode *root = char_data->get_model_root(mn); 01485 if (root->is_of_type(EggTable::get_class_type())) { 01486 // This model represents an animation. 01487 EggData *data = char_data->get_egg_data(mn); 01488 string basename = data->get_egg_filename().get_basename_wo_extension(); 01489 PT(EggAnimPreload) anim_preload = new EggAnimPreload(basename); 01490 01491 int mi = char_data->get_model_index(mn); 01492 anim_preload->set_num_frames(char_data->get_num_frames(mi)); 01493 double frame_rate = char_data->get_frame_rate(mi); 01494 if (frame_rate != 0.0) { 01495 anim_preload->set_fps(frame_rate); 01496 } 01497 01498 anim_group->add_child(anim_preload); 01499 } 01500 } 01501 } 01502 01503 // Now go back through and copy the preload tables into each of the 01504 // model files. 01505 for (ci = 0; ci < num_characters; ++ci) { 01506 EggCharacterData *char_data = _collection->get_character(ci); 01507 01508 int num_models = char_data->get_num_models(); 01509 for (int mn = 0; mn < num_models; ++mn) { 01510 EggNode *root = char_data->get_model_root(mn); 01511 if (root->is_of_type(EggGroup::get_class_type())) { 01512 // This is a model file. Copy in the table. 01513 EggGroup *model_root = DCAST(EggGroup, root); 01514 EggGroup::const_iterator ci; 01515 for (ci = anim_group->begin(); ci != anim_group->end(); ++ci) { 01516 EggAnimPreload *anim_preload = DCAST(EggAnimPreload, *ci); 01517 PT(EggAnimPreload) new_anim_preload = new EggAnimPreload(*anim_preload); 01518 model_root->add_child(new_anim_preload); 01519 } 01520 } 01521 } 01522 } 01523 } 01524 01525 //////////////////////////////////////////////////////////////////// 01526 // Function: EggOptchar::do_defpose 01527 // Access: Private 01528 // Description: Sets the initial pose for the character(s). 01529 //////////////////////////////////////////////////////////////////// 01530 void EggOptchar:: 01531 do_defpose() { 01532 // Split out the defpose parameter. 01533 Filename egg_filename; 01534 size_t comma = _defpose.find(','); 01535 egg_filename = _defpose.substr(0, comma); 01536 01537 string frame_str; 01538 if (comma != string::npos) { 01539 frame_str = _defpose.substr(comma + 1); 01540 } 01541 frame_str = trim(frame_str); 01542 int frame = 0; 01543 if (!frame_str.empty()) { 01544 if (!string_to_int(frame_str, frame)) { 01545 nout << "Invalid integer in -defpose: " << frame_str << "\n"; 01546 return; 01547 } 01548 } 01549 01550 // Now find the named animation file in our egg list. 01551 int egg_index = -1; 01552 int num_eggs = _collection->get_num_eggs(); 01553 int i; 01554 01555 // First, look for an exact match. 01556 for (i = 0; i < num_eggs && egg_index == -1; ++i) { 01557 if (_collection->get_egg(i)->get_egg_filename() == egg_filename) { 01558 egg_index = i; 01559 } 01560 } 01561 01562 // Then, look for an inexact match. 01563 string egg_basename = egg_filename.get_basename_wo_extension(); 01564 for (i = 0; i < num_eggs && egg_index == -1; ++i) { 01565 if (_collection->get_egg(i)->get_egg_filename().get_basename_wo_extension() == egg_basename) { 01566 egg_index = i; 01567 } 01568 } 01569 01570 if (egg_index == -1) { 01571 // No joy. 01572 nout << "Egg file " << egg_filename << " named in -defpose, but does not appear on command line.\n"; 01573 return; 01574 } 01575 01576 EggData *egg_data = _collection->get_egg(egg_index); 01577 01578 if (_collection->get_num_models(egg_index) == 0) { 01579 nout << "Egg file " << egg_filename << " does not include any model or animation.\n"; 01580 return; 01581 } 01582 01583 // Now get the first model (or animation) named by this egg file. 01584 int mi = _collection->get_first_model_index(egg_index); 01585 EggCharacterData *ch = _collection->get_character_by_model_index(mi); 01586 EggJointData *root_joint = ch->get_root_joint(); 01587 01588 int anim_index = -1; 01589 for (i = 0; i < ch->get_num_models() && anim_index == -1; ++i) { 01590 if (ch->get_egg_data(i) == egg_data) { 01591 anim_index = i; 01592 } 01593 } 01594 01595 // This couldn't possibly fail, since we already checked this above. 01596 nassertv(anim_index != -1); 01597 01598 // Now we can recursively apply the default pose to the hierarchy. 01599 ch->get_root_joint()->apply_default_pose(anim_index, frame); 01600 } 01601 01602 int main(int argc, char *argv[]) { 01603 // A call to pystub() to force libpystub.so to be linked in. 01604 pystub(); 01605 01606 EggOptchar prog; 01607 prog.parse_command_line(argc, argv); 01608 prog.run(); 01609 return 0; 01610 }