46   add_path_replace_options();
    47   add_path_store_options();
    52   set_program_brief(
"optimizes character models and animations in .egg files");
    53   set_program_description
    54     (
"egg-optchar performs basic optimizations of a character model "    55      "and its associated animations, primarily by analyzing the "    56      "animation tables and removing unneeded joints and/or morphs.  "    57      "It can also perform basic restructuring operations on the "    58      "character hierarchy.");
    62      "List the joint hierarchy instead of performing any operations.",
    63      &EggOptchar::dispatch_none, &_list_hierarchy);
    67      "List the joint hierarchy along with an indication of the properties "    69      &EggOptchar::dispatch_none, &_list_hierarchy_v);
    73      "List the existing joint hierarchy as a series of -p joint,parent "    74      "commands, suitable for pasting into an egg-optchar command line.",
    75      &EggOptchar::dispatch_none, &_list_hierarchy_p);
    78     (
"keep", 
"joint[,joint...]", 0,
    79      "Keep the named joints (or sliders) in the character, even if they do "    80      "not appear to be needed by the animation.",
    81      &EggOptchar::dispatch_vector_string_comma, 
nullptr, &_keep_components);
    84     (
"drop", 
"joint[,joint...]", 0,
    85      "Removes the named joints or sliders, even if they appear to be needed.",
    86      &EggOptchar::dispatch_vector_string_comma, 
nullptr, &_drop_components);
    89     (
"expose", 
"joint[,joint...]", 0,
    90      "Expose the named joints by flagging them with a DCS attribute, so "    91      "each one can be found in the scene graph when the character is loaded, "    92      "and objects can be parented to it.  This implies -keep.",
    93      &EggOptchar::dispatch_vector_string_comma, 
nullptr, &_expose_components);
    96     (
"suppress", 
"joint[,joint...]", 0,
    97      "The opposite of suppress, this prevents the named joints from "    98      "being created with an implicit DCS attribute, even if they contain "    99      "rigid geometry.  The default is to create an implicit node for any "   100      "joint that contains rigid geometry, to take advantage of display "   101      "list and/or vertex buffer caching.  This does not imply -keep.",
   102      &EggOptchar::dispatch_vector_string_comma, 
nullptr, &_suppress_components);
   105     (
"flag", 
"node[,node...][=name]", 0,
   106      "Assign the indicated name to the geometry within the given nodes.  "   107      "This will make the geometry visible as a node in the resulting "   108      "character model when it is loaded in the scene graph (normally, "   109      "the node hierarchy is suppressed when loading characters).  This "   110      "is different from -expose in that it reveals geometry rather than "   111      "joints; the revealed node can be hidden or its attributes changed "   112      "at runtime, but it will be animated by its vertices, not the node, so "   113      "objects parented to this node will not inherit its animation.",
   114      &EggOptchar::dispatch_flag_groups, 
nullptr, &_flag_groups);
   117     (
"defpose", 
"anim.egg,frame", 0,
   118      "Specify the model's default pose.  The pose is taken "   119      "from the indicated frame of the named animation file (which must "   120      "also be named separately on the command line).  The "   121      "pose will be held by the model in "   122      "the absence of any animation, and need not be the same "   123      "pose in which the model was originally skinned.",
   124      &EggOptchar::dispatch_string, 
nullptr, &_defpose);
   128      "Add an <AnimPreload> entry for each animation to the model file(s).  "   129      "This can be used at runtime to support asynchronous "   130      "loading and binding of animation channels.",
   131      &EggOptchar::dispatch_none, &_preload);
   134     (
"zero", 
"joint[,hprxyzijkabc]", 0,
   135      "Zeroes out the animation channels for the named joint.  If "   136      "a subset of the component letters hprxyzijkabc is included, the "   137      "operation is restricted to just those components; otherwise the "   138      "entire transform is cleared.",
   139      &EggOptchar::dispatch_name_components, 
nullptr, &_zero_channels);
   143      "Keep all joints and sliders in the character, except those named "   144      "explicitly by -drop.",
   145      &EggOptchar::dispatch_none, &_keep_all);
   148     (
"p", 
"joint,parent", 0,
   149      "Moves the named joint under the named parent joint.  Use "   150      "\"-p joint,\" to reparent a joint to the root.  The joint transform "   151      "is recomputed appropriately under its new parent so that the animation "   152      "is not affected (the effect is similar to NodePath::wrt_reparent_to).",
   153      &EggOptchar::dispatch_vector_string_pair, 
nullptr, &_reparent_joints);
   156     (
"new", 
"joint,source", 0,
   157      "Creates a new joint under the named parent joint.  The new "   158      "joint will inherit the same net transform as its parent.",
   159      &EggOptchar::dispatch_vector_string_pair, 
nullptr, &_new_joints);
   162     (
"rename", 
"joint,newjoint", 0,
   163      "Renames the indicated joint, if present, to the given name.",
   164      &EggOptchar::dispatch_vector_string_pair, 
nullptr, &_rename_joints);
   169        "Computes the optimal joint hierarchy for the character by analyzing "   170        "all of the joint animation and reparenting joints to minimize "   171        "transformations.  This can repair skeletons that have been flattened "   172        "or whose hierarchy was otherwise damaged in conversion; it can also "   173        "detect joints that are constrained to follow other joints and should "   174        "therefore be parented to the master joints.  The result is a file "   175        "from which more joints may be successfully removed, that generally "   176        "compresses better and with fewer artifacts.  However, this is a "   177        "fairly expensive operation.",
   178        &EggOptchar::dispatch_none, &_optimal_hierarchy);
   183      "Quantize joint membership values to the given unit.  This is "   184      "the smallest significant change in joint membership.  There can "   185      "be a significant performance (and memory utilization) runtime "   186      "benefit for eliminating small differences in joint memberships "   187      "between neighboring vertices.  The default is 0.01; specifying "   188      "0 means to preserve the original values.",
   189      &EggOptchar::dispatch_double, 
nullptr, &_vref_quantum);
   192     (
"qa", 
"quantum[,hprxyzijkabc]", 0,
   193      "Quantizes animation channels to the given unit.  This rounds each "   194      "of the named components of all joints to the nearest multiple of unit.  "   195      "There is no performance benefit, and little compression benefit, "   196      "for doing this; and this may introduce visible artifacts to the "   197      "animation.  However, sometimes it is a useful tool for animation "   198      "analysis and comparison.  This option may be repeated several times "   199      "to quantize different channels by a different amount.",
   200      &EggOptchar::dispatch_double_components, 
nullptr, &_quantize_anims);
   203     (
"dart", 
"[default, sync, nosync, or structured]", 0,
   204      "change the dart value in the given eggs",
   205      &EggOptchar::dispatch_string, 
nullptr, &_dart_type);
   208   _optimal_hierarchy = 
false;
   209   _vref_quantum = 0.01;
   220   if (apply_user_reparents()) {
   221     nout << 
"Reparenting hierarchy.\n";
   227   if (!_zero_channels.empty()) {
   231   int num_characters = _collection->get_num_characters();
   235   for (ci = 0; ci < num_characters; ci++) {
   238     analyze_sliders(char_data);
   241   if (_list_hierarchy || _list_hierarchy_v) {
   242     for (ci = 0; ci < num_characters; ci++) {
   244       nout << 
"Character: " << char_data->get_name() << 
"\n";
   246       list_scalars(char_data, _list_hierarchy_v);
   250   } 
else if (_list_hierarchy_p) {
   251     for (ci = 0; ci < num_characters; ci++) {
   253       nout << 
"Character: " << char_data->get_name() << 
"\n";
   264     determine_removed_components();
   266     if (process_joints()) {
   285     if (!_flag_groups.empty()) {
   287       for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
   300     if (!_defpose.empty()) {
   304     if (!_dart_type.empty()) {
   306       for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
   307         change_dart_type(*ei, _dart_type);
   322   if (_list_hierarchy || _list_hierarchy_v || _list_hierarchy_p) {
   326   return EggCharacterFilter::handle_args(args);
   335 dispatch_vector_string_pair(
const string &opt, 
const string &arg, 
void *var) {
   336   StringPairs *ip = (StringPairs *)var;
   341   if (words.size() == 2) {
   349          << 
" requires a pair of strings separated by a comma.\n";
   364 dispatch_name_components(
const string &opt, 
const string &arg, 
void *var) {
   365   StringPairs *ip = (StringPairs *)var;
   371   if (words.size() == 1) {
   374   } 
else if (words.size() == 2) {
   380          << 
" requires a pair of strings separated by a comma.\n";
   385     sp._b = matrix_component_letters;
   387     for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) {
   388       if (strchr(matrix_component_letters, *si) == 
nullptr) {
   389         nout << 
"Not a standard matrix component: \"" << *si << 
"\"\n"   390              << 
"-" << opt << 
" requires a joint name followed by a set "   391              << 
"of component names.  The standard component names are \""   392              << matrix_component_letters << 
"\".\n";
   411 dispatch_double_components(
const string &opt, 
const string &arg, 
void *var) {
   412   DoubleStrings *ip = (DoubleStrings *)var;
   417   bool valid_double = 
false;
   420   if (words.size() == 1) {
   423   } 
else if (words.size() == 2) {
   429          << 
" requires a numeric value followed by a string.\n";
   435          << 
" requires a numeric value followed by a string.\n";
   440     sp._b = matrix_component_letters;
   442     for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) {
   443       if (strchr(matrix_component_letters, *si) == 
nullptr) {
   444         nout << 
"Not a standard matrix component: \"" << *si << 
"\"\n"   445              << 
"-" << opt << 
" requires a joint name followed by a set "   446              << 
"of component names.  The standard component names are \""   447              << matrix_component_letters << 
"\".\n";
   465 dispatch_flag_groups(
const string &opt, 
const string &arg, 
void *var) {
   466   FlagGroups *ip = (FlagGroups *)var;
   474          << 
" requires a series of words separated by a comma.\n";
   478   FlagGroupsEntry entry;
   481   string &last_word = words.back();
   482   size_t equals = last_word.rfind(
'=');
   483   if (equals != string::npos) {
   484     entry._name = last_word.substr(equals + 1);
   485     last_word = last_word.substr(0, equals);
   493   vector_string::const_iterator si;
   494   for (si = words.begin(); si != words.end(); ++si) {
   495     const string &word = (*si);
   499   ip->push_back(entry);
   509 determine_removed_components() {
   514   Names suppress_names;
   517   vector_string::const_iterator si;
   518   for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
   519     keep_names.insert(*si);
   521   for (si = _drop_components.begin(); si != _drop_components.end(); ++si) {
   522     drop_names.insert(*si);
   524   for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
   525     keep_names.insert(*si);
   526     expose_names.insert(*si);
   528   for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) {
   529     suppress_names.insert(*si);
   533   keep_names.insert(
"");
   535   int num_characters = _collection->get_num_characters();
   536   for (
int ci = 0; ci < num_characters; ci++) {
   539     nout << char_data->get_name() << 
" has " << num_components << 
" components.\n";
   540     for (
int i = 0; i < num_components; i++) {
   542       nassertv(comp_data != 
nullptr);
   546       nassertv(user_data != 
nullptr);
   548       const string &name = comp_data->get_name();
   549       if (suppress_names.find(name) != suppress_names.end()) {
   552         names_used.insert(name);
   553         user_data->_flags |= EggOptcharUserData::F_suppress;
   556       if (drop_names.find(name) != drop_names.end()) {
   558         names_used.insert(name);
   559         user_data->_flags |= EggOptcharUserData::F_remove;
   561       } 
else if (_keep_all || keep_names.find(name) != keep_names.end()) {
   563         names_used.insert(name);
   565         if (expose_names.find(name) != expose_names.end()) {
   567           user_data->_flags |= EggOptcharUserData::F_expose;
   572         if ((user_data->_flags & (EggOptcharUserData::F_static | EggOptcharUserData::F_empty)) != 0) {
   573           if ((user_data->_flags & (EggOptcharUserData::F_top | EggOptcharUserData::F_empty)) == EggOptcharUserData::F_top) {
   583             user_data->_flags |= EggOptcharUserData::F_remove;
   592   for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
   593     const string &name = (*si);
   594     if (names_used.find(name) == names_used.end()) {
   595       nout << 
"No such component: " << name << 
"\n";
   598   for (si = _drop_components.begin(); si != _drop_components.end(); ++si) {
   599     const string &name = (*si);
   600     if (names_used.find(name) == names_used.end()) {
   601       nout << 
"No such component: " << name << 
"\n";
   604   for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
   605     const string &name = (*si);
   606     if (names_used.find(name) == names_used.end()) {
   607       nout << 
"No such component: " << name << 
"\n";
   610   for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) {
   611     const string &name = (*si);
   612     if (names_used.find(name) == names_used.end()) {
   613       nout << 
"No such component: " << name << 
"\n";
   625   int num_characters = _collection->get_num_characters();
   626   for (
int ci = 0; ci < num_characters; ci++) {
   630     for (
int i = 0; i < num_joints; i++) {
   635       if ((user_data->_flags & EggOptcharUserData::F_empty) == 0 &&
   636           (user_data->_flags & EggOptcharUserData::F_remove) != 0) {
   639         EggJointData *best_joint = find_best_vertex_joint(joint_data->get_parent());
   643         if (best_joint != 
nullptr) {
   646           best_user_data->_flags &= ~(EggOptcharUserData::F_empty | EggOptcharUserData::F_remove);
   661   bool removed_any = 
false;
   662   int num_characters = _collection->get_num_characters();
   663   for (
int ci = 0; ci < num_characters; ci++) {
   669     int num_identity = 0;
   673     for (
int i = 0; i < num_joints; i++) {
   678       if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
   684         if ((user_data->_flags & EggOptcharUserData::F_identity) != 0) {
   686         } 
else if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
   688         } 
else if ((user_data->_flags & EggOptcharUserData::F_empty) != 0) {
   697         EggJointData *best_parent = find_best_parent(joint_data->get_parent());
   699         if ((user_data->_flags & EggOptcharUserData::F_expose) != 0) {
   701         } 
else if ((user_data->_flags & EggOptcharUserData::F_suppress) != 0) {
   702           joint_data->
expose(EggGroup::DC_none);
   708     if (num_joints == num_kept) {
   709       nout << char_data->get_name() << 
": keeping " << num_joints
   712       nout << setw(5) << num_joints
   713            << 
" original joints in " << char_data->get_name()
   715       if (num_identity != 0) {
   716         nout << setw(5) << num_identity << 
" identity joints\n";
   718       if (num_static != 0) {
   719         nout << setw(5) << num_static << 
" unanimated joints\n";
   721       if (num_empty != 0) {
   722         nout << setw(5) << num_empty << 
" empty joints\n";
   724       if (num_other != 0) {
   725         nout << setw(5) << num_other << 
" other joints\n";
   728            << setw(5) << num_kept << 
" joints remaining\n\n";
   745   if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
   747     if (joint_data->get_parent() != 
nullptr) {
   748       return find_best_parent(joint_data->get_parent());
   761 find_best_vertex_joint(
EggJointData *joint_data)
 const {
   762   if (joint_data == 
nullptr) {
   769   if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
   771     return find_best_vertex_joint(joint_data->get_parent());
   783 apply_user_reparents() {
   784   bool did_anything = 
false;
   786   int num_characters = _collection->get_num_characters();
   789   StringPairs::const_iterator spi;
   790   for (spi = _new_joints.begin(); spi != _new_joints.end(); ++spi) {
   791     const StringPair &p = (*spi);
   793     for (
int ci = 0; ci < num_characters; ci++) {
   801       if (node_b == 
nullptr) {
   802         nout << 
"No joint named " << p._b << 
" in " << char_data->get_name()
   805       } 
else if (node_a != 
nullptr) {
   806         nout << 
"Joint " << p._a << 
" already exists in "   807              << char_data->get_name() << 
".\n";
   810         nout << 
"Creating new joint " << p._a << 
" in "   811              << char_data->get_name() << 
".\n";
   819   for (spi = _reparent_joints.begin(); spi != _reparent_joints.end(); ++spi) {
   820     const StringPair &p = (*spi);
   822     for (
int ci = 0; ci < num_characters; ci++) {
   830       if (node_b == 
nullptr) {
   831         nout << 
"No joint named " << p._b << 
" in " << char_data->get_name()
   833       } 
else if (node_a == 
nullptr) {
   834         nout << 
"No joint named " << p._a << 
" in " << char_data->get_name()
   843   if (_optimal_hierarchy) {
   845     for (
int ci = 0; ci < num_characters; ci++) {
   847       nout << 
"Computing optimal hierarchy for "   848            << char_data->get_name() << 
".\n";
   850       nout << 
"Done computing optimal hierarchy for "   851            << char_data->get_name() << 
".\n";
   865   bool did_anything = 
false;
   866   int num_characters = _collection->get_num_characters();
   868   StringPairs::const_iterator spi;
   869   for (spi = _zero_channels.begin(); spi != _zero_channels.end(); ++spi) {
   870     const StringPair &p = (*spi);
   872     for (
int ci = 0; ci < num_characters; ci++) {
   876       if (joint_data == 
nullptr) {
   877         nout << 
"No joint named " << p._a << 
" in " << char_data->get_name()
   895 quantize_channels() {
   896   bool did_anything = 
false;
   897   int num_characters = _collection->get_num_characters();
   899   DoubleStrings::const_iterator spi;
   900   for (spi = _quantize_anims.begin(); spi != _quantize_anims.end(); ++spi) {
   901     const DoubleString &p = (*spi);
   903     for (
int ci = 0; ci < num_characters; ci++) {
   907       if (joint_data != 
nullptr) {
   930     user_data->_flags |= EggOptcharUserData::F_top;
   937   bool different_mat = 
false;
   938   bool has_vertices = 
false;
   942   for (i = 0; i < num_models; i++) {
   952       for (f = 0; f < num_frames && !different_mat; f++) {
   953         LMatrix4d mat = joint_data->
get_frame(i, f);
   957           user_data->_static_mat = mat;
   961           if (!mat.almost_equal(user_data->_static_mat, 0.0001)) {
   963             different_mat = 
true;
   970   if (!different_mat) {
   972     user_data->_flags |= EggOptcharUserData::F_static;
   975         user_data->_static_mat.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
   977       user_data->_flags |= EggOptcharUserData::F_identity;
   983     user_data->_flags |= EggOptcharUserData::F_empty;
   986   int num_children = joint_data->get_num_children();
   987   for (i = 0; i < num_children; i++) {
   988     analyze_joints(joint_data->get_child(i), level + 1);
   999   for (
int si = 0; si < num_sliders; si++) {
  1009     bool different_value = 
false;
  1010     bool has_vertices = 
false;
  1013     for (
int i = 0; i < num_models; i++) {
  1017           has_vertices = 
true;
  1023         for (f = 0; f < num_frames && !different_value; f++) {
  1024           double value = slider_data->
get_frame(i, f);
  1026           if (num_values == 1) {
  1028             user_data->_static_value = value;
  1032             if (!IS_THRESHOLD_EQUAL(value, user_data->_static_value, 0.0001)) {
  1034               different_value = 
true;
  1041     if (!different_value) {
  1043       user_data->_flags |= EggOptcharUserData::F_static;
  1045       if (num_values == 0 || IS_THRESHOLD_ZERO(user_data->_static_value, 0.0001)) {
  1047         user_data->_flags |= EggOptcharUserData::F_identity;
  1051     if (!has_vertices) {
  1053       user_data->_flags |= EggOptcharUserData::F_empty;
  1062 list_joints(
EggJointData *joint_data, 
int indent_level, 
bool verbose) {
  1066   int num_children = joint_data->get_num_children();
  1067   for (
int i = 0; i < num_children; i++) {
  1069     describe_component(child_data, indent_level, verbose);
  1071     list_joints(child_data, indent_level + 2, verbose);
  1083   int num_children = joint_data->get_num_children();
  1084   static const int max_col = 72;
  1086   for (
int i = 0; i < num_children; i++) {
  1091     string text = string(
" -p ") + child_data->get_name() +
  1092       string(
",") + joint_data->get_name();
  1094       cout << 
"    " << text;
  1095       col = 4 + text.length();
  1097       col += text.length();
  1098       if (col >= max_col) {
  1099         cout << 
" \\\n    " << text;
  1100         col = 4 + text.length();
  1106     list_joints_p(child_data, col);
  1116   for (
int si = 0; si < num_sliders; si++) {
  1118     describe_component(slider_data, 0, verbose);
  1130   indent(cout, indent_level)
  1131     << comp_data->get_name();
  1136     if (user_data->is_identity()) {
  1137       cout << 
" (identity)";
  1138     } 
else if (user_data->is_static()) {
  1139       cout << 
" (static)";
  1141     if (user_data->is_empty()) {
  1144     if (user_data->is_top()) {
  1158   int num_characters = _collection->get_num_characters();
  1159   for (
int ci = 0; ci < num_characters; ci++) {
  1176 quantize_vertices() {
  1178   for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
  1179     quantize_vertices(*ei);
  1188 quantize_vertices(
EggNode *egg_node) {
  1189   if (egg_node->
is_of_type(EggVertexPool::get_class_type())) {
  1192     for (vi = vpool->
begin(); vi != vpool->
end(); ++vi) {
  1193       quantize_vertex(*vi);
  1196   } 
else if (egg_node->
is_of_type(EggGroupNode::get_class_type())) {
  1198     EggGroupNode::iterator ci;
  1199     for (ci = group->begin(); ci != group->end(); ++ci) {
  1200       quantize_vertices(*ci);
  1209 quantize_vertex(
EggVertex *egg_vertex) {
  1217   EggVertex::GroupRef::const_iterator gi;
  1218   double net_membership = 0.0;
  1223     net_membership += membership;
  1225   nassertv(net_membership != 0.0);
  1229   double factor = 1.0 / net_membership;
  1230   net_membership = 0.0;
  1231   VertexMemberships::iterator mi;
  1232   VertexMemberships::iterator largest = memberships.begin();
  1234   for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
  1235     if ((*largest) < (*mi)) {
  1241     double value = (*mi)._membership * factor;
  1242     if (_vref_quantum != 0.0) {
  1243       value = floor(value / _vref_quantum + 0.5) * _vref_quantum;
  1245     (*mi)._membership = value;
  1247     net_membership += value;
  1252   (*largest)._membership += 1.0 - net_membership;
  1255   for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
  1256     (*mi)._group->set_vertex_membership(egg_vertex, (*mi)._membership);
  1267   bool matched = 
false;
  1269   FlagGroups::const_iterator fi;
  1270   for (fi = _flag_groups.begin();
  1271        fi != _flag_groups.end() && !matched;
  1273     const FlagGroupsEntry &entry = (*fi);
  1274     Globs::const_iterator si;
  1275     for (si = entry._groups.begin();
  1276          si != entry._groups.end() && !matched;
  1278       if ((*si).matches(egg_group->get_name())) {
  1280         if (!entry._name.empty()) {
  1283           name = egg_group->get_name();
  1293     rename_primitives(egg_group, name);
  1297   EggGroupNode::iterator gi;
  1298   for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
  1300     if (child->
is_of_type(EggGroupNode::get_class_type())) {
  1302       do_flag_groups(group);
  1312   for (StringPairs::iterator spi = _rename_joints.begin();
  1313        spi != _rename_joints.end();
  1315     const StringPair &sp = (*spi);
  1316     int num_characters = _collection->get_num_characters();
  1318     for (ci = 0; ci < num_characters; ++ci) {
  1321       if (joint != 
nullptr) {
  1322         nout << 
"Renaming joint " << sp._a << 
" to " << sp._b << 
"\n";
  1323         joint->set_name(sp._b);
  1326         for (
int mn = 0; mn < num_models; ++mn) {
  1334         nout << 
"Couldn't find joint " << sp._a << 
"\n";
  1345 change_dart_type(
EggGroupNode *egg_group, 
const string &new_dart_type) {
  1346   EggGroupNode::iterator gi;
  1347   for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
  1349     if (child->
is_of_type(EggGroupNode::get_class_type())) {
  1351       if (child->
is_of_type(EggGroup::get_class_type())) {
  1353         EggGroup::DartType dt = gr->get_dart_type();
  1354         if(dt != EggGroup::DT_none) {
  1356           gr->set_dart_type(newDt);
  1359       change_dart_type(group, new_dart_type);
  1370 rename_primitives(
EggGroupNode *egg_group, 
const string &name) {
  1371   EggGroupNode::iterator gi;
  1372   for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
  1375     if (child->
is_of_type(EggGroupNode::get_class_type())) {
  1377       rename_primitives(group, name);
  1379     } 
else if (child->
is_of_type(EggPrimitive::get_class_type())) {
  1380       child->set_name(name);
  1394   int num_characters = _collection->get_num_characters();
  1396   for (ci = 0; ci < num_characters; ++ci) {
  1400     for (
int mn = 0; mn < num_models; ++mn) {
  1402       if (root->
is_of_type(EggTable::get_class_type())) {
  1405         string basename = data->get_egg_filename().get_basename_wo_extension();
  1411         if (frame_rate != 0.0) {
  1412           anim_preload->set_fps(frame_rate);
  1415         anim_group->add_child(anim_preload);
  1422   for (ci = 0; ci < num_characters; ++ci) {
  1426     for (
int mn = 0; mn < num_models; ++mn) {
  1428       if (root->
is_of_type(EggGroup::get_class_type())) {
  1431         EggGroup::const_iterator ci;
  1432         for (ci = anim_group->begin(); ci != anim_group->end(); ++ci) {
  1435           model_root->
add_child(new_anim_preload);
  1449   size_t comma = _defpose.find(
',');
  1450   egg_filename = _defpose.substr(0, comma);
  1453   if (comma != string::npos) {
  1454     frame_str = _defpose.substr(comma + 1);
  1456   frame_str = 
trim(frame_str);
  1458   if (!frame_str.empty()) {
  1460       nout << 
"Invalid integer in -defpose: " << frame_str << 
"\n";
  1467   int num_eggs = _collection->get_num_eggs();
  1471   for (i = 0; i < num_eggs && egg_index == -1; ++i) {
  1472     if (_collection->get_egg(i)->get_egg_filename() == egg_filename) {
  1479   for (i = 0; i < num_eggs && egg_index == -1; ++i) {
  1480     if (_collection->get_egg(i)->get_egg_filename().get_basename_wo_extension() == egg_basename) {
  1485   if (egg_index == -1) {
  1487     nout << 
"Egg file " << egg_filename << 
" named in -defpose, but does not appear on command line.\n";
  1491   EggData *egg_data = _collection->get_egg(egg_index);
  1493   if (_collection->get_num_models(egg_index) == 0) {
  1494     nout << 
"Egg file " << egg_filename << 
" does not include any model or animation.\n";
  1499   int mi = _collection->get_first_model_index(egg_index);
  1503   int anim_index = -1;
  1511   nassertv(anim_index != -1);
  1517 int main(
int argc, 
char *argv[]) {
 int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
void reparent_to(EggJointData *new_parent)
Indicates an intention to change the parent of this joint to the indicated joint, or NULL to remove i...
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static DartType string_dart_type(const std::string &strval)
Returns the DartType value associated with the given string representation, or DT_none if the string ...
int get_num_frames(int model_index) const
Returns the number of frames of animation for this particular component in the indicated model.
GroupRef::const_iterator gref_end() const
Returns an iterator that can, in conjunction with gref_begin(), be used to traverse the entire set of...
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_num_joints() const
Returns the total number of joints in the character joint hierarchy.
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
bool do_reparent()
Begins the process of restructuring the joint hierarchy according to the previous calls to reparent_t...
int get_num_frames(int model_index) const
Returns the number of frames of animation of the indicated model.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
A base class for nodes in the hierarchy that are not leaf nodes.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
void add_normals_options()
Adds -no, -np, etc.
int get_num_models() const
Returns the maximum number of back pointers this component may have.
virtual void set_name(const std::string &name)
Applies the indicated name change to the egg file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GroupRef::const_iterator gref_begin() const
Returns an iterator that can, in conjunction with gref_end(), be used to traverse the entire set of g...
This corresponds to a single morph slider control.
int get_num_models() const
Returns the total number of models associated with this character.
EggBackPointer * get_model(int model_index) const
Returns the back pointer to an egg file for the indicated model if it exists, or NULL if it does not.
This is the primary interface into all the egg data, and the root of the egg file structure.
EggJointData * make_new_joint(const std::string &name, EggJointData *parent)
Creates a new joint as a child of the indicated joint and returns it.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
iterator end() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
EggData * get_egg_data(int n) const
Returns the EggData representing the egg file that defined this particular model.
This is the base class of both EggJointData and EggSliderData.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool has_vertices() const
Returns true if there are any vertices referenced by the node this points to, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
void apply_default_pose(int source_model, int frame)
Applies the pose from the indicated frame of the indicated source model_index as the initial pose for...
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
The name of a file, such as a texture file or an Egg file.
This corresponds to an <AnimPreload> entry.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggSliderData * get_slider(int n) const
Returns the nth slider in the character slider list.
Performs basic optimizations of a character model and its associated animations, by analyzing the ani...
EggComponentData * get_component(int n) const
Returns the nth joint or slider in the character.
EggUserData * get_user_data() const
Returns the user data pointer most recently stored on this object, or NULL if nothing was previously ...
void move_vertices_to(EggJointData *new_owner)
Moves the vertices assigned to this joint into the indicated joint, without changing their weight ass...
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
This class contains extra user data which is piggybacked onto EggGroup objects for the purpose of the...
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
bool has_model(int model_index) const
Returns true if the component has a back pointer to an egg file somewhere for the indicated model,...
int get_num_components() const
Returns the total number of joints and sliders in the character.
void choose_optimal_hierarchy()
Chooses the best possible parent joint for each of the joints in the hierarchy, based on the score co...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNode * get_model_root(int n) const
Returns the model_root of the nth model associated with this character.
Represents a single character, as read and collected from several models and animation files.
This class is used to help EggOptchar quantize the membership of one vertex among its various groups.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_user_data(EggUserData *user_data)
Sets the user data associated with this object.
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
EggJointData * find_joint(const std::string &name) const
Returns the first joint found with the indicated name, or NULL if no joint has that name.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
EggJointData * get_joint(int n) const
Returns the nth joint in the character joint hierarchy.
A base class for things that may be directly added into the egg hierarchy.
int get_num_sliders() const
Returns the number of sliders in the character slider list.
LMatrix4d get_frame(int model_index, int n) const
Returns the local transform matrix corresponding to this joint position in the nth frame in the indic...
void add_transform_options()
Adds -TS, -TT, etc.
void zero_channels(const std::string &components)
Calls zero_channels() on all models for this joint, but does not recurse downwards.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void quantize_channels(const std::string &components, double quantum)
Calls quantize_channels() on all models for this joint, and then recurses downwards to all joints bel...
double get_frame(int model_index, int n) const
Returns the value corresponding to this slider position in the nth frame in the indicated model.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
This is our own Panda specialization on the default STL set.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double get_frame_rate(int model_index) const
Returns the stated frame rate of the specified model.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This stores a pointer from an EggJointData or EggSliderData object back to the referencing data in an...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void expose(EggGroup::DCSType dcs_type=EggGroup::DC_default)
Calls expose() on all models for this joint, but does not recurse downwards.
A collection of vertices.
static bool is_compression_available()
Returns true if the FFTW library is compiled in, so that this class is actually capable of doing usef...
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
int get_model_index(int n) const
Returns the model_index of the nth model associated with this character.