Panda3D

eggOptchar.cxx

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 }
 All Classes Functions Variables Enumerations