00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00041
00042
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
00213
00214
00215
00216 void EggOptchar::
00217 run() {
00218
00219
00220
00221 if (apply_user_reparents()) {
00222 nout << "Reparenting hierarchy.\n";
00223
00224
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
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
00258 cout << "\n";
00259 nout << char_data->get_num_joints() << " joints.\n";
00260 }
00261
00262 } else {
00263
00264
00265 determine_removed_components();
00266 move_vertices();
00267 if (process_joints()) {
00268 do_reparent();
00269 }
00270
00271
00272
00273
00274
00275
00276 rename_joints();
00277
00278
00279
00280
00281 quantize_vertices();
00282
00283
00284 quantize_channels();
00285
00286
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
00295 if (_preload) {
00296 do_preload();
00297 }
00298
00299
00300
00301
00302
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
00320
00321
00322
00323
00324
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
00337
00338
00339
00340
00341
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
00367
00368
00369
00370
00371
00372
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
00416
00417
00418
00419
00420
00421
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
00473
00474
00475
00476
00477
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
00496
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
00505
00506
00507 }
00508
00509
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
00523
00524
00525
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
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
00570
00571 names_used.insert(name);
00572 user_data->_flags |= EggOptcharUserData::F_suppress;
00573 }
00574
00575 if (drop_names.find(name) != drop_names.end()) {
00576
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
00582 names_used.insert(name);
00583
00584 if (expose_names.find(name) != expose_names.end()) {
00585
00586 user_data->_flags |= EggOptcharUserData::F_expose;
00587 }
00588
00589 } else {
00590
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
00594
00595
00596
00597
00598
00599
00600 } else {
00601
00602
00603 user_data->_flags |= EggOptcharUserData::F_remove;
00604 }
00605 }
00606 }
00607 }
00608 }
00609
00610
00611
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
00640
00641
00642
00643
00644
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
00661
00662 EggJointData *best_joint = find_best_vertex_joint(joint_data->get_parent());
00663 joint_data->move_vertices_to(best_joint);
00664
00665
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
00679
00680
00681
00682
00683
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
00706 joint_data->reparent_to((EggJointData *)NULL);
00707
00708
00709
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
00723
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
00764
00765
00766
00767
00768
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
00777 if (joint_data->get_parent() != (EggJointData *)NULL) {
00778 return find_best_parent(joint_data->get_parent());
00779 }
00780 }
00781
00782
00783 return joint_data;
00784 }
00785
00786
00787
00788
00789
00790
00791
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
00804 return find_best_vertex_joint(joint_data->get_parent());
00805 }
00806
00807
00808 return joint_data;
00809 }
00810
00811
00812
00813
00814
00815
00816
00817
00818 bool EggOptchar::
00819 apply_user_reparents() {
00820 bool did_anything = false;
00821
00822 int num_characters = _collection->get_num_characters();
00823
00824
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
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
00896
00897
00898
00899
00900
00901
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
00931
00932
00933
00934
00935
00936
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
00963
00964
00965
00966
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
00975
00976
00977 user_data->_flags |= EggOptcharUserData::F_top;
00978 }
00979
00980
00981
00982
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
01004 user_data->_static_mat = mat;
01005
01006 } else {
01007
01008 if (!mat.almost_equal(user_data->_static_mat, 0.0001)) {
01009
01010 different_mat = true;
01011 }
01012 }
01013 }
01014 }
01015 }
01016
01017 if (!different_mat) {
01018
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
01024 user_data->_flags |= EggOptcharUserData::F_identity;
01025 }
01026 }
01027
01028 if (!has_vertices) {
01029
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
01041
01042
01043
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
01055
01056
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
01077 user_data->_static_value = value;
01078
01079 } else {
01080
01081 if (!IS_THRESHOLD_EQUAL(value, user_data->_static_value, 0.0001)) {
01082
01083 different_value = true;
01084 }
01085 }
01086 }
01087 }
01088 }
01089
01090 if (!different_value) {
01091
01092 user_data->_flags |= EggOptcharUserData::F_static;
01093
01094 if (num_values == 0 || IS_THRESHOLD_ZERO(user_data->_static_value, 0.0001)) {
01095
01096 user_data->_flags |= EggOptcharUserData::F_identity;
01097 }
01098 }
01099
01100 if (!has_vertices) {
01101
01102 user_data->_flags |= EggOptcharUserData::F_empty;
01103 }
01104 }
01105 }
01106
01107
01108
01109
01110
01111
01112 void EggOptchar::
01113 list_joints(EggJointData *joint_data, int indent_level, bool verbose) {
01114
01115
01116
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
01129
01130
01131
01132
01133 void EggOptchar::
01134 list_joints_p(EggJointData *joint_data, int &col) {
01135
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
01143
01144
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
01167
01168
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
01181
01182
01183
01184 void EggOptchar::
01185 describe_component(EggComponentData *comp_data, int indent_level,
01186 bool verbose) {
01187
01188
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
01212
01213
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
01234
01235
01236
01237
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
01249
01250
01251
01252
01253
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
01275
01276
01277
01278 void EggOptchar::
01279 quantize_vertex(EggVertex *egg_vertex) {
01280 if (egg_vertex->gref_size() == 0) {
01281
01282 return;
01283 }
01284
01285
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
01298
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
01307
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
01321
01322 (*largest)._membership += 1.0 - net_membership;
01323
01324
01325
01326 for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
01327 (*mi)._group->set_vertex_membership(egg_vertex, (*mi)._membership);
01328 }
01329 }
01330
01331
01332
01333
01334
01335
01336
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
01364
01365
01366
01367 rename_primitives(egg_group, name);
01368 }
01369
01370
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
01383
01384
01385
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
01419
01420
01421
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
01446
01447
01448
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
01468
01469
01470
01471 void EggOptchar::
01472 do_preload() {
01473
01474
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
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
01504
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
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
01527
01528
01529
01530 void EggOptchar::
01531 do_defpose() {
01532
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
01551 int egg_index = -1;
01552 int num_eggs = _collection->get_num_eggs();
01553 int i;
01554
01555
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
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
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
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
01596 nassertv(anim_index != -1);
01597
01598
01599 ch->get_root_joint()->apply_default_pose(anim_index, frame);
01600 }
01601
01602 int main(int argc, char *argv[]) {
01603
01604 pystub();
01605
01606 EggOptchar prog;
01607 prog.parse_command_line(argc, argv);
01608 prog.run();
01609 return 0;
01610 }