Panda3D
eggOptchar.cxx
1 // Filename: eggOptchar.cxx
2 // Created by: drose (18Jul03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "eggOptchar.h"
16 #include "eggOptcharUserData.h"
17 #include "vertexMembership.h"
18 
19 #include "eggJointData.h"
20 #include "eggSliderData.h"
21 #include "eggCharacterCollection.h"
22 #include "eggCharacterData.h"
23 #include "eggBackPointer.h"
24 #include "eggGroupNode.h"
25 #include "eggPrimitive.h"
26 #include "eggVertexPool.h"
27 #include "eggTable.h"
28 #include "eggGroup.h"
29 #include "eggAnimPreload.h"
30 #include "string_utils.h"
31 #include "dcast.h"
32 #include "pset.h"
33 #include "compose_matrix.h"
34 #include "fftCompressor.h"
35 #include "pystub.h"
36 
37 #include <algorithm>
38 
39 ////////////////////////////////////////////////////////////////////
40 // Function: EggOptchar::Constructor
41 // Access: Public
42 // Description:
43 ////////////////////////////////////////////////////////////////////
44 EggOptchar::
45 EggOptchar() {
46  add_path_replace_options();
47  add_path_store_options();
50  add_fixrest_option();
51 
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.");
59 
60  add_option
61  ("ls", "", 0,
62  "List the joint hierarchy instead of performing any operations.",
63  &EggOptchar::dispatch_none, &_list_hierarchy);
64 
65  add_option
66  ("lsv", "", 0,
67  "List the joint hierarchy along with an indication of the properties "
68  "each joint.",
69  &EggOptchar::dispatch_none, &_list_hierarchy_v);
70 
71  add_option
72  ("lsp", "", 0,
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);
76 
77  add_option
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, NULL, &_keep_components);
82 
83  add_option
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, NULL, &_drop_components);
87 
88  add_option
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, NULL, &_expose_components);
94 
95  add_option
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, NULL, &_suppress_components);
103 
104  add_option
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, NULL, &_flag_groups);
115 
116  add_option
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, NULL, &_defpose);
125 
126  add_option
127  ("preload", "", 0,
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);
132 
133  add_option
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, NULL, &_zero_channels);
140 
141  add_option
142  ("keepall", "", 0,
143  "Keep all joints and sliders in the character, except those named "
144  "explicitly by -drop.",
145  &EggOptchar::dispatch_none, &_keep_all);
146 
147  add_option
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, NULL, &_reparent_joints);
154 
155  add_option
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, NULL, &_new_joints);
160 
161  add_option
162  ("rename", "joint,newjoint", 0,
163  "Renames the indicated joint, if present, to the given name.",
164  &EggOptchar::dispatch_vector_string_pair, NULL, &_rename_joints);
165 
167  add_option
168  ("optimal", "", 0,
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);
179  }
180 
181  add_option
182  ("q", "quantum", 0,
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, NULL, &_vref_quantum);
190 
191  add_option
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, NULL, &_quantize_anims);
201 
202  add_option
203  ("dart", "[default, sync, nosync, or structured]", 0,
204  "change the dart value in the given eggs",
205  &EggOptchar::dispatch_string, NULL, &_dart_type);
206 
207 
208  _optimal_hierarchy = false;
209  _vref_quantum = 0.01;
210 }
211 
212 ////////////////////////////////////////////////////////////////////
213 // Function: EggOptchar::run
214 // Access: Public
215 // Description:
216 ////////////////////////////////////////////////////////////////////
217 void EggOptchar::
218 run() {
219  // We have to apply the user-specified reparent requests first,
220  // before we even analyze the joints. This is because reparenting
221  // the joints may change their properties.
222  if (apply_user_reparents()) {
223  nout << "Reparenting hierarchy.\n";
224  // So we'll have to call do_reparent() twice. It seems wasteful,
225  // but it really is necessary, and it's not that bad.
226  do_reparent();
227  }
228 
229  if (!_zero_channels.empty()) {
230  zero_channels();
231  }
232 
233  int num_characters = _collection->get_num_characters();
234  int ci;
235 
236  // Now we can analyze the joints for their properties.
237  for (ci = 0; ci < num_characters; ci++) {
238  EggCharacterData *char_data = _collection->get_character(ci);
239  analyze_joints(char_data->get_root_joint(), 0);
240  analyze_sliders(char_data);
241  }
242 
243  if (_list_hierarchy || _list_hierarchy_v) {
244  for (ci = 0; ci < num_characters; ci++) {
245  EggCharacterData *char_data = _collection->get_character(ci);
246  nout << "Character: " << char_data->get_name() << "\n";
247  list_joints(char_data->get_root_joint(), 0, _list_hierarchy_v);
248  list_scalars(char_data, _list_hierarchy_v);
249  nout << char_data->get_num_joints() << " joints.\n";
250  }
251 
252  } else if (_list_hierarchy_p) {
253  for (ci = 0; ci < num_characters; ci++) {
254  EggCharacterData *char_data = _collection->get_character(ci);
255  nout << "Character: " << char_data->get_name() << "\n";
256  int col = 0;
257  list_joints_p(char_data->get_root_joint(), col);
258  // A newline to cout is needed after the above call.
259  cout << "\n";
260  nout << char_data->get_num_joints() << " joints.\n";
261  }
262 
263  } else {
264  // The meat of the program: determine which joints are to be
265  // removed, and then actually remove them.
266  determine_removed_components();
267  move_vertices();
268  if (process_joints()) {
269  do_reparent();
270  }
271 
272  // We currently do not implement optimizing morph sliders. Need
273  // to add this at some point; it's quite easy. Identity and empty
274  // morph sliders can simply be removed, while static sliders need
275  // to be applied to the vertices and then removed.
276 
277  rename_joints();
278 
279  // Quantize the vertex memberships. We call this even if
280  // _vref_quantum is 0, because this also normalizes the vertex
281  // memberships.
282  quantize_vertices();
283 
284  // Also quantize the animation channels, if the user so requested.
285  quantize_channels();
286 
287  // flag all the groups as the user requested.
288  if (!_flag_groups.empty()) {
289  Eggs::iterator ei;
290  for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
291  do_flag_groups(*ei);
292  }
293  }
294 
295  // Add the AnimPreload entries.
296  if (_preload) {
297  do_preload();
298  }
299 
300 
301  // Finally, set the default poses. It's important not to do this
302  // until after we have adjusted all of the transforms for the
303  // various joints.
304  if (!_defpose.empty()) {
305  do_defpose();
306  }
307 
308  if (!_dart_type.empty()) {
309  Eggs::iterator ei;
310  for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
311  change_dart_type(*ei, _dart_type);
312  }
313  }
314 
315  write_eggs();
316  }
317 }
318 
319 ////////////////////////////////////////////////////////////////////
320 // Function: EggOptchar::handle_args
321 // Access: Protected, Virtual
322 // Description: Does something with the additional arguments on the
323 // command line (after all the -options have been
324 // parsed). Returns true if the arguments are good,
325 // false otherwise.
326 ////////////////////////////////////////////////////////////////////
327 bool EggOptchar::
328 handle_args(ProgramBase::Args &args) {
329  if (_list_hierarchy || _list_hierarchy_v || _list_hierarchy_p) {
330  _read_only = true;
331  }
332 
333  return EggCharacterFilter::handle_args(args);
334 }
335 
336 ////////////////////////////////////////////////////////////////////
337 // Function: ProgramBase::dispatch_vector_string_pair
338 // Access: Protected, Static
339 // Description: Standard dispatch function for an option that takes
340 // a pair of string parameters. The data pointer is to
341 // StringPairs vector; the pair will be pushed onto the
342 // end of the vector.
343 ////////////////////////////////////////////////////////////////////
344 bool EggOptchar::
345 dispatch_vector_string_pair(const string &opt, const string &arg, void *var) {
346  StringPairs *ip = (StringPairs *)var;
347 
348  vector_string words;
349  tokenize(arg, words, ",");
350 
351  if (words.size() == 2) {
352  StringPair sp;
353  sp._a = words[0];
354  sp._b = words[1];
355  ip->push_back(sp);
356 
357  } else {
358  nout << "-" << opt
359  << " requires a pair of strings separated by a comma.\n";
360  return false;
361  }
362 
363  return true;
364 }
365 
366 ////////////////////////////////////////////////////////////////////
367 // Function: ProgramBase::dispatch_name_components
368 // Access: Protected, Static
369 // Description: Accepts a name optionally followed by a comma and some
370 // of the nine standard component letters,
371 //
372 // The data pointer is to StringPairs vector; the pair
373 // will be pushed onto the end of the vector.
374 ////////////////////////////////////////////////////////////////////
375 bool EggOptchar::
376 dispatch_name_components(const string &opt, const string &arg, void *var) {
377  StringPairs *ip = (StringPairs *)var;
378 
379  vector_string words;
380  tokenize(arg, words, ",");
381 
382  StringPair sp;
383  if (words.size() == 1) {
384  sp._a = words[0];
385 
386  } else if (words.size() == 2) {
387  sp._a = words[0];
388  sp._b = words[1];
389 
390  } else {
391  nout << "-" << opt
392  << " requires a pair of strings separated by a comma.\n";
393  return false;
394  }
395 
396  if (sp._b.empty()) {
397  sp._b = matrix_component_letters;
398  } else {
399  for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) {
400  if (strchr(matrix_component_letters, *si) == NULL) {
401  nout << "Not a standard matrix component: \"" << *si << "\"\n"
402  << "-" << opt << " requires a joint name followed by a set "
403  << "of component names. The standard component names are \""
404  << matrix_component_letters << "\".\n";
405  return false;
406  }
407  }
408  }
409 
410  ip->push_back(sp);
411 
412  return true;
413 }
414 
415 ////////////////////////////////////////////////////////////////////
416 // Function: ProgramBase::dispatch_double_components
417 // Access: Protected, Static
418 // Description: Accepts a double value optionally followed by a comma
419 // and some of the nine standard component letters,
420 //
421 // The data pointer is to a DoubleStrings vector; the
422 // pair will be pushed onto the end of the vector.
423 ////////////////////////////////////////////////////////////////////
424 bool EggOptchar::
425 dispatch_double_components(const string &opt, const string &arg, void *var) {
426  DoubleStrings *ip = (DoubleStrings *)var;
427 
428  vector_string words;
429  tokenize(arg, words, ",");
430 
431  bool valid_double = false;
432 
433  DoubleString sp;
434  if (words.size() == 1) {
435  valid_double = string_to_double(words[0], sp._a);
436 
437  } else if (words.size() == 2) {
438  valid_double = string_to_double(words[0], sp._a);
439  sp._b = words[1];
440 
441  } else {
442  nout << "-" << opt
443  << " requires a numeric value followed by a string.\n";
444  return false;
445  }
446 
447  if (!valid_double) {
448  nout << "-" << opt
449  << " requires a numeric value followed by a string.\n";
450  return false;
451  }
452 
453  if (sp._b.empty()) {
454  sp._b = matrix_component_letters;
455  } else {
456  for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) {
457  if (strchr(matrix_component_letters, *si) == NULL) {
458  nout << "Not a standard matrix component: \"" << *si << "\"\n"
459  << "-" << opt << " requires a joint name followed by a set "
460  << "of component names. The standard component names are \""
461  << matrix_component_letters << "\".\n";
462  return false;
463  }
464  }
465  }
466 
467  ip->push_back(sp);
468 
469  return true;
470 }
471 
472 ////////////////////////////////////////////////////////////////////
473 // Function: ProgramBase::dispatch_flag_groups
474 // Access: Protected, Static
475 // Description: Accepts a set of comma-delimited group names followed
476 // by an optional name separated with an equal sign.
477 //
478 // The data pointer is to a FlagGroups object.
479 ////////////////////////////////////////////////////////////////////
480 bool EggOptchar::
481 dispatch_flag_groups(const string &opt, const string &arg, void *var) {
482  FlagGroups *ip = (FlagGroups *)var;
483 
484  vector_string words;
485 
486  tokenize(arg, words, ",");
487 
488  if (words.empty()) {
489  nout << "-" << opt
490  << " requires a series of words separated by a comma.\n";
491  return false;
492  }
493 
494  FlagGroupsEntry entry;
495 
496  // Check for an equal sign in the last word. This marks the name to
497  // assign.
498  string &last_word = words.back();
499  size_t equals = last_word.rfind('=');
500  if (equals != string::npos) {
501  entry._name = last_word.substr(equals + 1);
502  last_word = last_word.substr(0, equals);
503 
504  } else {
505  // If there's no equal sign, the default is to name all groups
506  // after the group itself. We leave the name empty to indicate
507  // that.
508  }
509 
510  // Convert the words to GlobPatterns.
511  vector_string::const_iterator si;
512  for (si = words.begin(); si != words.end(); ++si) {
513  const string &word = (*si);
514  entry._groups.push_back(GlobPattern(word));
515  }
516 
517  ip->push_back(entry);
518 
519  return true;
520 }
521 
522 ////////////////////////////////////////////////////////////////////
523 // Function: EggOptchar::determine_removed_components
524 // Access: Private
525 // Description: Flag all joints and sliders that should be removed
526 // for optimization purposes.
527 ////////////////////////////////////////////////////////////////////
528 void EggOptchar::
529 determine_removed_components() {
530  typedef pset<string> Names;
531  Names keep_names;
532  Names drop_names;
533  Names expose_names;
534  Names suppress_names;
535  Names names_used;
536 
537  vector_string::const_iterator si;
538  for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
539  keep_names.insert(*si);
540  }
541  for (si = _drop_components.begin(); si != _drop_components.end(); ++si) {
542  drop_names.insert(*si);
543  }
544  for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
545  keep_names.insert(*si);
546  expose_names.insert(*si);
547  }
548  for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) {
549  suppress_names.insert(*si);
550  }
551 
552  // We always keep the root joint, which has no name.
553  keep_names.insert("");
554 
555  int num_characters = _collection->get_num_characters();
556  for (int ci = 0; ci < num_characters; ci++) {
557  EggCharacterData *char_data = _collection->get_character(ci);
558  int num_components = char_data->get_num_components();
559  nout << char_data->get_name() << " has " << num_components << " components.\n";
560  for (int i = 0; i < num_components; i++) {
561  EggComponentData *comp_data = char_data->get_component(i);
562  nassertv(comp_data != (EggComponentData *)NULL);
563 
564  EggOptcharUserData *user_data =
565  DCAST(EggOptcharUserData, comp_data->get_user_data());
566  nassertv(user_data != (EggOptcharUserData *)NULL);
567 
568  const string &name = comp_data->get_name();
569  if (suppress_names.find(name) != suppress_names.end()) {
570  // If this component is not dropped, it will not be implicitly
571  // exposed.
572  names_used.insert(name);
573  user_data->_flags |= EggOptcharUserData::F_suppress;
574  }
575 
576  if (drop_names.find(name) != drop_names.end()) {
577  // Remove this component by user request.
578  names_used.insert(name);
579  user_data->_flags |= EggOptcharUserData::F_remove;
580 
581  } else if (_keep_all || keep_names.find(name) != keep_names.end()) {
582  // Keep this component.
583  names_used.insert(name);
584 
585  if (expose_names.find(name) != expose_names.end()) {
586  // In fact, expose it.
587  user_data->_flags |= EggOptcharUserData::F_expose;
588  }
589 
590  } else {
591  // Remove this component if it's unanimated or empty.
592  if ((user_data->_flags & (EggOptcharUserData::F_static | EggOptcharUserData::F_empty)) != 0) {
593  if ((user_data->_flags & (EggOptcharUserData::F_top | EggOptcharUserData::F_empty)) == EggOptcharUserData::F_top) {
594  // Actually, we can't remove it if it's a top joint,
595  // unless it's also empty. That's because vertices that
596  // are partially assigned to this joint would then have no
597  // joint to represent the same partial assignment, and
598  // they would then appear to be wholly assigned to their
599  // other joint, which would be incorrect.
600 
601  } else {
602  // But joints that aren't top joints (or that are empty)
603  // are o.k. to remove.
604  user_data->_flags |= EggOptcharUserData::F_remove;
605  }
606  }
607  }
608  }
609  }
610 
611  // Go back and tell the user about component names we didn't use,
612  // just to be helpful.
613  for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
614  const string &name = (*si);
615  if (names_used.find(name) == names_used.end()) {
616  nout << "No such component: " << name << "\n";
617  }
618  }
619  for (si = _drop_components.begin(); si != _drop_components.end(); ++si) {
620  const string &name = (*si);
621  if (names_used.find(name) == names_used.end()) {
622  nout << "No such component: " << name << "\n";
623  }
624  }
625  for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
626  const string &name = (*si);
627  if (names_used.find(name) == names_used.end()) {
628  nout << "No such component: " << name << "\n";
629  }
630  }
631  for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) {
632  const string &name = (*si);
633  if (names_used.find(name) == names_used.end()) {
634  nout << "No such component: " << name << "\n";
635  }
636  }
637 }
638 
639 ////////////////////////////////////////////////////////////////////
640 // Function: EggOptchar::move_vertices
641 // Access: Private
642 // Description: Moves the vertices from joints that are about to be
643 // removed into the first suitable parent. This might
644 // result in fewer joints being removed (because
645 // the parent might suddenly no longer be empty).
646 ////////////////////////////////////////////////////////////////////
647 void EggOptchar::
648 move_vertices() {
649  int num_characters = _collection->get_num_characters();
650  for (int ci = 0; ci < num_characters; ci++) {
651  EggCharacterData *char_data = _collection->get_character(ci);
652  int num_joints = char_data->get_num_joints();
653 
654  for (int i = 0; i < num_joints; i++) {
655  EggJointData *joint_data = char_data->get_joint(i);
656  EggOptcharUserData *user_data =
657  DCAST(EggOptcharUserData, joint_data->get_user_data());
658 
659  if ((user_data->_flags & EggOptcharUserData::F_empty) == 0 &&
660  (user_data->_flags & EggOptcharUserData::F_remove) != 0) {
661  // This joint has vertices, but is scheduled to be removed;
662  // find a suitable home for its vertices.
663  EggJointData *best_joint = find_best_vertex_joint(joint_data->get_parent());
664  joint_data->move_vertices_to(best_joint);
665 
666  // Now we can't remove the joint.
667  if (best_joint != (EggJointData *)NULL) {
668  EggOptcharUserData *best_user_data =
669  DCAST(EggOptcharUserData, best_joint->get_user_data());
670  best_user_data->_flags &= ~(EggOptcharUserData::F_empty | EggOptcharUserData::F_remove);
671  }
672  }
673  }
674  }
675 }
676 
677 
678 ////////////////////////////////////////////////////////////////////
679 // Function: EggOptchar::process_joints
680 // Access: Private
681 // Description: Effects the actual removal of joints flagged for
682 // removal by reparenting the hierarchy appropriately.
683 // Returns true if any joints are removed, false
684 // otherwise.
685 ////////////////////////////////////////////////////////////////////
686 bool EggOptchar::
687 process_joints() {
688  bool removed_any = false;
689  int num_characters = _collection->get_num_characters();
690  for (int ci = 0; ci < num_characters; ci++) {
691  EggCharacterData *char_data = _collection->get_character(ci);
692  int num_joints = char_data->get_num_joints();
693 
694  int num_static = 0;
695  int num_empty = 0;
696  int num_identity = 0;
697  int num_other = 0;
698  int num_kept = 0;
699 
700  for (int i = 0; i < num_joints; i++) {
701  EggJointData *joint_data = char_data->get_joint(i);
702  EggOptcharUserData *user_data =
703  DCAST(EggOptcharUserData, joint_data->get_user_data());
704 
705  if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
706  // This joint will be removed, so reparent it to nothing.
707  joint_data->reparent_to((EggJointData *)NULL);
708 
709  // Determine what kind of node it is we're removing, for the
710  // user's information.
711  if ((user_data->_flags & EggOptcharUserData::F_identity) != 0) {
712  num_identity++;
713  } else if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
714  num_static++;
715  } else if ((user_data->_flags & EggOptcharUserData::F_empty) != 0) {
716  num_empty++;
717  } else {
718  num_other++;
719  }
720  removed_any = true;
721 
722  } else {
723  // This joint will be preserved, but maybe its parent will
724  // change.
725  EggJointData *best_parent = find_best_parent(joint_data->get_parent());
726  joint_data->reparent_to(best_parent);
727  if ((user_data->_flags & EggOptcharUserData::F_expose) != 0) {
728  joint_data->expose();
729  } else if ((user_data->_flags & EggOptcharUserData::F_suppress) != 0) {
730  joint_data->expose(EggGroup::DC_none);
731  }
732  num_kept++;
733  }
734  }
735 
736  if (num_joints == num_kept) {
737  nout << char_data->get_name() << ": keeping " << num_joints
738  << " joints.\n";
739  } else {
740  nout << setw(5) << num_joints
741  << " original joints in " << char_data->get_name()
742  << "\n";
743  if (num_identity != 0) {
744  nout << setw(5) << num_identity << " identity joints\n";
745  }
746  if (num_static != 0) {
747  nout << setw(5) << num_static << " unanimated joints\n";
748  }
749  if (num_empty != 0) {
750  nout << setw(5) << num_empty << " empty joints\n";
751  }
752  if (num_other != 0) {
753  nout << setw(5) << num_other << " other joints\n";
754  }
755  nout << " ----\n"
756  << setw(5) << num_kept << " joints remaining\n\n";
757  }
758  }
759 
760  return removed_any;
761 }
762 
763 ////////////////////////////////////////////////////////////////////
764 // Function: EggOptchar::find_best_parent
765 // Access: Private
766 // Description: Searches for the first joint at this level or above
767 // that is not scheduled to be removed. This is the
768 // joint that the first child of this joint should be
769 // reparented to.
770 ////////////////////////////////////////////////////////////////////
771 EggJointData *EggOptchar::
772 find_best_parent(EggJointData *joint_data) const {
773  EggOptcharUserData *user_data =
774  DCAST(EggOptcharUserData, joint_data->get_user_data());
775 
776  if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
777  // Keep going.
778  if (joint_data->get_parent() != (EggJointData *)NULL) {
779  return find_best_parent(joint_data->get_parent());
780  }
781  }
782 
783  // This is the one!
784  return joint_data;
785 }
786 
787 ////////////////////////////////////////////////////////////////////
788 // Function: EggOptchar::find_best_vertex_joint
789 // Access: Private
790 // Description: Searches for the first joint at this level or above
791 // that is not static. This is the joint that the
792 // vertices of this joint should be moved into.
793 ////////////////////////////////////////////////////////////////////
794 EggJointData *EggOptchar::
795 find_best_vertex_joint(EggJointData *joint_data) const {
796  if (joint_data == (EggJointData *)NULL) {
797  return NULL;
798  }
799 
800  EggOptcharUserData *user_data =
801  DCAST(EggOptcharUserData, joint_data->get_user_data());
802 
803  if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
804  // Keep going.
805  return find_best_vertex_joint(joint_data->get_parent());
806  }
807 
808  // This is the one!
809  return joint_data;
810 }
811 
812 ////////////////////////////////////////////////////////////////////
813 // Function: EggOptchar::apply_user_reparents
814 // Access: Private
815 // Description: Reparents all the joints that the user suggested on
816 // the command line. Returns true if any operations
817 // were performed, false otherwise.
818 ////////////////////////////////////////////////////////////////////
819 bool EggOptchar::
820 apply_user_reparents() {
821  bool did_anything = false;
822 
823  int num_characters = _collection->get_num_characters();
824 
825  // First, get the new joints.
826  StringPairs::const_iterator spi;
827  for (spi = _new_joints.begin(); spi != _new_joints.end(); ++spi) {
828  const StringPair &p = (*spi);
829 
830  for (int ci = 0; ci < num_characters; ci++) {
831  EggCharacterData *char_data = _collection->get_character(ci);
832  EggJointData *node_a = char_data->find_joint(p._a);
833  EggJointData *node_b = char_data->get_root_joint();
834  if (!p._b.empty()) {
835  node_b = char_data->find_joint(p._b);
836  }
837 
838  if (node_b == (EggJointData *)NULL) {
839  nout << "No joint named " << p._b << " in " << char_data->get_name()
840  << ".\n";
841 
842  } else if (node_a != (EggJointData *)NULL) {
843  nout << "Joint " << p._a << " already exists in "
844  << char_data->get_name() << ".\n";
845 
846  } else {
847  nout << "Creating new joint " << p._a << " in "
848  << char_data->get_name() << ".\n";
849  node_a = char_data->make_new_joint(p._a, node_b);
850  did_anything = true;
851  }
852  }
853  }
854 
855  // Now get the user reparents.
856  for (spi = _reparent_joints.begin(); spi != _reparent_joints.end(); ++spi) {
857  const StringPair &p = (*spi);
858 
859  for (int ci = 0; ci < num_characters; ci++) {
860  EggCharacterData *char_data = _collection->get_character(ci);
861  EggJointData *node_a = char_data->find_joint(p._a);
862  EggJointData *node_b = char_data->get_root_joint();
863  if (!p._b.empty()) {
864  node_b = char_data->find_joint(p._b);
865  }
866 
867  if (node_b == (EggJointData *)NULL) {
868  nout << "No joint named " << p._b << " in " << char_data->get_name()
869  << ".\n";
870  } else if (node_a == (EggJointData *)NULL) {
871  nout << "No joint named " << p._a << " in " << char_data->get_name()
872  << ".\n";
873  } else {
874  node_a->reparent_to(node_b);
875  did_anything = true;
876  }
877  }
878  }
879 
880  if (_optimal_hierarchy) {
881  did_anything = true;
882  for (int ci = 0; ci < num_characters; ci++) {
883  EggCharacterData *char_data = _collection->get_character(ci);
884  nout << "Computing optimal hierarchy for "
885  << char_data->get_name() << ".\n";
886  char_data->choose_optimal_hierarchy();
887  nout << "Done computing optimal hierarchy for "
888  << char_data->get_name() << ".\n";
889  }
890  }
891 
892  return did_anything;
893 }
894 
895 ////////////////////////////////////////////////////////////////////
896 // Function: EggOptchar::zero_channels
897 // Access: Private
898 // Description: Zeroes out the channels specified by the user on the
899 // command line.
900 //
901 // Returns true if any operation was performed, false
902 // otherwise.
903 ////////////////////////////////////////////////////////////////////
904 bool EggOptchar::
905 zero_channels() {
906  bool did_anything = false;
907  int num_characters = _collection->get_num_characters();
908 
909  StringPairs::const_iterator spi;
910  for (spi = _zero_channels.begin(); spi != _zero_channels.end(); ++spi) {
911  const StringPair &p = (*spi);
912 
913  for (int ci = 0; ci < num_characters; ci++) {
914  EggCharacterData *char_data = _collection->get_character(ci);
915  EggJointData *joint_data = char_data->find_joint(p._a);
916 
917  if (joint_data == (EggJointData *)NULL) {
918  nout << "No joint named " << p._a << " in " << char_data->get_name()
919  << ".\n";
920  } else {
921  joint_data->zero_channels(p._b);
922  did_anything = true;
923  }
924  }
925  }
926 
927  return did_anything;
928 }
929 
930 ////////////////////////////////////////////////////////////////////
931 // Function: EggOptchar::quantize_channels
932 // Access: Private
933 // Description: Quantizes the channels specified by the user on the
934 // command line.
935 //
936 // Returns true if any operation was performed, false
937 // otherwise.
938 ////////////////////////////////////////////////////////////////////
939 bool EggOptchar::
940 quantize_channels() {
941  bool did_anything = false;
942  int num_characters = _collection->get_num_characters();
943 
944  DoubleStrings::const_iterator spi;
945  for (spi = _quantize_anims.begin(); spi != _quantize_anims.end(); ++spi) {
946  const DoubleString &p = (*spi);
947 
948  for (int ci = 0; ci < num_characters; ci++) {
949  EggCharacterData *char_data = _collection->get_character(ci);
950  EggJointData *joint_data = char_data->get_root_joint();
951 
952  if (joint_data != (EggJointData *)NULL) {
953  joint_data->quantize_channels(p._b, p._a);
954  did_anything = true;
955  }
956  }
957  }
958 
959  return did_anything;
960 }
961 
962 ////////////////////////////////////////////////////////////////////
963 // Function: EggOptchar::analyze_joints
964 // Access: Private
965 // Description: Recursively walks the joint hierarchy for a
966 // particular character, indentifying properties of each
967 // joint.
968 ////////////////////////////////////////////////////////////////////
969 void EggOptchar::
970 analyze_joints(EggJointData *joint_data, int level) {
971  PT(EggOptcharUserData) user_data = new EggOptcharUserData;
972  joint_data->set_user_data(user_data);
973 
974  if (level == 1) {
975  // The child joints of the root joint are deemed "top" joints.
976  // These may not be removed unless they are empty (because their
977  // vertices have no joint to be moved into).
978  user_data->_flags |= EggOptcharUserData::F_top;
979  }
980 
981  // Analyze the table of matrices for this joint, checking to see if
982  // they're all the same across all frames, or if any of them are
983  // different; also look for empty joints (that control no vertices).
984  int num_mats = 0;
985  bool different_mat = false;
986  bool has_vertices = false;
987 
988  int num_models = joint_data->get_num_models();
989  int i;
990  for (i = 0; i < num_models; i++) {
991  if (joint_data->has_model(i)) {
992  EggBackPointer *model = joint_data->get_model(i);
993  if (model->has_vertices()) {
994  has_vertices = true;
995  }
996 
997  int num_frames = joint_data->get_num_frames(i);
998 
999  int f;
1000  for (f = 0; f < num_frames && !different_mat; f++) {
1001  LMatrix4d mat = joint_data->get_frame(i, f);
1002  num_mats++;
1003  if (num_mats == 1) {
1004  // This is the first matrix.
1005  user_data->_static_mat = mat;
1006 
1007  } else {
1008  // This is a second or later matrix.
1009  if (!mat.almost_equal(user_data->_static_mat, 0.0001)) {
1010  // It's different than the first one.
1011  different_mat = true;
1012  }
1013  }
1014  }
1015  }
1016  }
1017 
1018  if (!different_mat) {
1019  // All the mats are the same for this joint.
1020  user_data->_flags |= EggOptcharUserData::F_static;
1021 
1022  if (num_mats == 0 ||
1023  user_data->_static_mat.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
1024  // It's not only static, but it's the identity matrix.
1025  user_data->_flags |= EggOptcharUserData::F_identity;
1026  }
1027  }
1028 
1029  if (!has_vertices) {
1030  // There are no vertices in this joint.
1031  user_data->_flags |= EggOptcharUserData::F_empty;
1032  }
1033 
1034  int num_children = joint_data->get_num_children();
1035  for (i = 0; i < num_children; i++) {
1036  analyze_joints(joint_data->get_child(i), level + 1);
1037  }
1038 }
1039 
1040 ////////////////////////////////////////////////////////////////////
1041 // Function: EggOptchar::analyze_sliders
1042 // Access: Private
1043 // Description: Linearly walks the slider list for a particular
1044 // character, indentifying properties of each slider.
1045 ////////////////////////////////////////////////////////////////////
1046 void EggOptchar::
1047 analyze_sliders(EggCharacterData *char_data) {
1048  int num_sliders = char_data->get_num_sliders();
1049  for (int si = 0; si < num_sliders; si++) {
1050  EggSliderData *slider_data = char_data->get_slider(si);
1051 
1052  PT(EggOptcharUserData) user_data = new EggOptcharUserData;
1053  slider_data->set_user_data(user_data);
1054 
1055  // Analyze the table of values for this slider, checking to see if
1056  // they're all the same across all frames, or if any of them are
1057  // different; also look for empty sliders (that control no vertices).
1058  int num_values = 0;
1059  bool different_value = false;
1060  bool has_vertices = false;
1061 
1062  int num_models = slider_data->get_num_models();
1063  for (int i = 0; i < num_models; i++) {
1064  if (slider_data->has_model(i)) {
1065  EggBackPointer *model = slider_data->get_model(i);
1066  if (model->has_vertices()) {
1067  has_vertices = true;
1068  }
1069 
1070  int num_frames = slider_data->get_num_frames(i);
1071 
1072  int f;
1073  for (f = 0; f < num_frames && !different_value; f++) {
1074  double value = slider_data->get_frame(i, f);
1075  num_values++;
1076  if (num_values == 1) {
1077  // This is the first value.
1078  user_data->_static_value = value;
1079 
1080  } else {
1081  // This is a second or later value.
1082  if (!IS_THRESHOLD_EQUAL(value, user_data->_static_value, 0.0001)) {
1083  // It's different than the first one.
1084  different_value = true;
1085  }
1086  }
1087  }
1088  }
1089  }
1090 
1091  if (!different_value) {
1092  // All the values are the same for this slider.
1093  user_data->_flags |= EggOptcharUserData::F_static;
1094 
1095  if (num_values == 0 || IS_THRESHOLD_ZERO(user_data->_static_value, 0.0001)) {
1096  // It's not only static, but it's the identity value.
1097  user_data->_flags |= EggOptcharUserData::F_identity;
1098  }
1099  }
1100 
1101  if (!has_vertices) {
1102  // There are no vertices in this slider.
1103  user_data->_flags |= EggOptcharUserData::F_empty;
1104  }
1105  }
1106 }
1107 
1108 ////////////////////////////////////////////////////////////////////
1109 // Function: EggOptchar::list_joints
1110 // Access: Private
1111 // Description: Outputs a list of the joint hierarchy.
1112 ////////////////////////////////////////////////////////////////////
1113 void EggOptchar::
1114 list_joints(EggJointData *joint_data, int indent_level, bool verbose) {
1115  // Don't list the root joint, which is artificially created when the
1116  // character is loaded. Instead, list each child as it is
1117  // encountered.
1118 
1119  int num_children = joint_data->get_num_children();
1120  for (int i = 0; i < num_children; i++) {
1121  EggJointData *child_data = joint_data->get_child(i);
1122  describe_component(child_data, indent_level, verbose);
1123 
1124  list_joints(child_data, indent_level + 2, verbose);
1125  }
1126 }
1127 
1128 ////////////////////////////////////////////////////////////////////
1129 // Function: EggOptchar::list_joints_p
1130 // Access: Private
1131 // Description: Outputs a list of the joint hierarchy as a series of
1132 // -p joint,parent commands.
1133 ////////////////////////////////////////////////////////////////////
1134 void EggOptchar::
1135 list_joints_p(EggJointData *joint_data, int &col) {
1136  // As above, don't list the root joint.
1137 
1138  int num_children = joint_data->get_num_children();
1139  static const int max_col = 72;
1140 
1141  for (int i = 0; i < num_children; i++) {
1142  EggJointData *child_data = joint_data->get_child(i);
1143  // We send output to cout instead of nout to avoid the
1144  // word-wrapping, and also to allow the user to redirect this
1145  // easily to a file.
1146 
1147  string text = string(" -p ") + child_data->get_name() +
1148  string(",") + joint_data->get_name();
1149  if (col == 0) {
1150  cout << " " << text;
1151  col = 4 + text.length();
1152  } else {
1153  col += text.length();
1154  if (col >= max_col) {
1155  cout << " \\\n " << text;
1156  col = 4 + text.length();
1157  } else {
1158  cout << text;
1159  }
1160  }
1161 
1162  list_joints_p(child_data, col);
1163  }
1164 }
1165 
1166 ////////////////////////////////////////////////////////////////////
1167 // Function: EggOptchar::list_scalars
1168 // Access: Private
1169 // Description: Outputs a list of the scalars.
1170 ////////////////////////////////////////////////////////////////////
1171 void EggOptchar::
1172 list_scalars(EggCharacterData *char_data, bool verbose) {
1173  int num_sliders = char_data->get_num_sliders();
1174  for (int si = 0; si < num_sliders; si++) {
1175  EggSliderData *slider_data = char_data->get_slider(si);
1176  describe_component(slider_data, 0, verbose);
1177  }
1178 }
1179 
1180 ////////////////////////////////////////////////////////////////////
1181 // Function: EggOptchar::describe_component
1182 // Access: Private
1183 // Description: Describes one particular slider or joint.
1184 ////////////////////////////////////////////////////////////////////
1185 void EggOptchar::
1186 describe_component(EggComponentData *comp_data, int indent_level,
1187  bool verbose) {
1188  // We use cout instead of nout so the user can easily redirect this
1189  // to a file.
1190  indent(cout, indent_level)
1191  << comp_data->get_name();
1192 
1193  if (verbose) {
1194  EggOptcharUserData *user_data =
1195  DCAST(EggOptcharUserData, comp_data->get_user_data());
1196  if (user_data->is_identity()) {
1197  cout << " (identity)";
1198  } else if (user_data->is_static()) {
1199  cout << " (static)";
1200  }
1201  if (user_data->is_empty()) {
1202  cout << " (empty)";
1203  }
1204  if (user_data->is_top()) {
1205  cout << " (top)";
1206  }
1207  }
1208  cout << "\n";
1209 }
1210 
1211 ////////////////////////////////////////////////////////////////////
1212 // Function: EggOptchar::do_reparent
1213 // Access: Private
1214 // Description: Performs all of the queued up reparenting operations.
1215 ////////////////////////////////////////////////////////////////////
1216 void EggOptchar::
1217 do_reparent() {
1218  bool all_ok = true;
1219 
1220  int num_characters = _collection->get_num_characters();
1221  for (int ci = 0; ci < num_characters; ci++) {
1222  EggCharacterData *char_data = _collection->get_character(ci);
1223  if (!char_data->do_reparent()) {
1224  all_ok = false;
1225  }
1226  }
1227 
1228  if (!all_ok) {
1229  exit(1);
1230  }
1231 }
1232 
1233 ////////////////////////////////////////////////////////////////////
1234 // Function: EggOptchar::quantize_vertices
1235 // Access: Private
1236 // Description: Walks through all of the loaded egg files, looking
1237 // for vertices whose joint memberships are then
1238 // quantized according to _vref_quantum.
1239 ////////////////////////////////////////////////////////////////////
1240 void EggOptchar::
1241 quantize_vertices() {
1242  Eggs::iterator ei;
1243  for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
1244  quantize_vertices(*ei);
1245  }
1246 }
1247 
1248 ////////////////////////////////////////////////////////////////////
1249 // Function: EggOptchar::quantize_vertices
1250 // Access: Private
1251 // Description: Recursively walks through the indicated egg
1252 // hierarchy, looking for vertices whose joint
1253 // memberships are then quantized according to
1254 // _vref_quantum.
1255 ////////////////////////////////////////////////////////////////////
1256 void EggOptchar::
1257 quantize_vertices(EggNode *egg_node) {
1258  if (egg_node->is_of_type(EggVertexPool::get_class_type())) {
1259  EggVertexPool *vpool = DCAST(EggVertexPool, egg_node);
1261  for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
1262  quantize_vertex(*vi);
1263  }
1264 
1265  } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
1266  EggGroupNode *group = DCAST(EggGroupNode, egg_node);
1267  EggGroupNode::iterator ci;
1268  for (ci = group->begin(); ci != group->end(); ++ci) {
1269  quantize_vertices(*ci);
1270  }
1271  }
1272 }
1273 
1274 ////////////////////////////////////////////////////////////////////
1275 // Function: EggOptchar::quantize_vertex
1276 // Access: Private
1277 // Description: Quantizes the indicated vertex's joint membership.
1278 ////////////////////////////////////////////////////////////////////
1279 void EggOptchar::
1280 quantize_vertex(EggVertex *egg_vertex) {
1281  if (egg_vertex->gref_size() == 0) {
1282  // Never mind on this vertex.
1283  return;
1284  }
1285 
1286  // First, get a copy of the existing membership.
1287  VertexMemberships memberships;
1288  EggVertex::GroupRef::const_iterator gi;
1289  double net_membership = 0.0;
1290  for (gi = egg_vertex->gref_begin(); gi != egg_vertex->gref_end(); ++gi) {
1291  EggGroup *group = (*gi);
1292  double membership = group->get_vertex_membership(egg_vertex);
1293  memberships.push_back(VertexMembership(group, membership));
1294  net_membership += membership;
1295  }
1296  nassertv(net_membership != 0.0);
1297 
1298  // Now normalize all the memberships so the net membership is 1.0,
1299  // and then quantize the result (if the user so requested).
1300  double factor = 1.0 / net_membership;
1301  net_membership = 0.0;
1302  VertexMemberships::iterator mi;
1303  VertexMemberships::iterator largest = memberships.begin();
1304 
1305  for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
1306  if ((*largest) < (*mi)) {
1307  // Remember the largest membership value, so we can readjust it
1308  // at the end.
1309  largest = mi;
1310  }
1311 
1312  double value = (*mi)._membership * factor;
1313  if (_vref_quantum != 0.0) {
1314  value = floor(value / _vref_quantum + 0.5) * _vref_quantum;
1315  }
1316  (*mi)._membership = value;
1317 
1318  net_membership += value;
1319  }
1320 
1321  // The the largest membership value gets corrected again by the
1322  // roundoff error.
1323  (*largest)._membership += 1.0 - net_membership;
1324 
1325  // Finally, walk back through and apply these computed values to the
1326  // vertex.
1327  for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
1328  (*mi)._group->set_vertex_membership(egg_vertex, (*mi)._membership);
1329  }
1330 }
1331 
1332 ////////////////////////////////////////////////////////////////////
1333 // Function: EggOptchar::do_flag_groups
1334 // Access: Private
1335 // Description: Recursively walks the indicated egg hierarchy,
1336 // looking for groups that match one of the group names
1337 // in _flag_groups, and renaming geometry appropriately.
1338 ////////////////////////////////////////////////////////////////////
1339 void EggOptchar::
1340 do_flag_groups(EggGroupNode *egg_group) {
1341  bool matched = false;
1342  string name;
1343  FlagGroups::const_iterator fi;
1344  for (fi = _flag_groups.begin();
1345  fi != _flag_groups.end() && !matched;
1346  ++fi) {
1347  const FlagGroupsEntry &entry = (*fi);
1348  Globs::const_iterator si;
1349  for (si = entry._groups.begin();
1350  si != entry._groups.end() && !matched;
1351  ++si) {
1352  if ((*si).matches(egg_group->get_name())) {
1353  matched = true;
1354  if (!entry._name.empty()) {
1355  name = entry._name;
1356  } else {
1357  name = egg_group->get_name();
1358  }
1359  }
1360  }
1361  }
1362 
1363  if (matched) {
1364  // Ok, this group matched one of the user's command-line renames.
1365  // Rename all the primitives in this group and below to the
1366  // indicated name; this will expose the primitives through the
1367  // character loader.
1368  rename_primitives(egg_group, name);
1369  }
1370 
1371  // Now recurse on children.
1372  EggGroupNode::iterator gi;
1373  for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
1374  EggNode *child = (*gi);
1375  if (child->is_of_type(EggGroupNode::get_class_type())) {
1376  EggGroupNode *group = DCAST(EggGroupNode, child);
1377  do_flag_groups(group);
1378  }
1379  }
1380 }
1381 
1382 ////////////////////////////////////////////////////////////////////
1383 // Function: EggOptchar::rename_joints
1384 // Access: Private
1385 // Description: Rename all the joints named with the -rename
1386 // command-line option.
1387 ////////////////////////////////////////////////////////////////////
1388 void EggOptchar::
1389 rename_joints() {
1390  for (StringPairs::iterator spi = _rename_joints.begin();
1391  spi != _rename_joints.end();
1392  ++spi) {
1393  const StringPair &sp = (*spi);
1394  int num_characters = _collection->get_num_characters();
1395  int ci;
1396  for (ci = 0; ci < num_characters; ++ci) {
1397  EggCharacterData *char_data = _collection->get_character(ci);
1398  EggJointData *joint = char_data->find_joint(sp._a);
1399  if (joint != (EggJointData *)NULL) {
1400  nout << "Renaming joint " << sp._a << " to " << sp._b << "\n";
1401  joint->set_name(sp._b);
1402 
1403  int num_models = joint->get_num_models();
1404  for (int mn = 0; mn < num_models; ++mn) {
1405  if (joint->has_model(mn)) {
1406  EggBackPointer *model = joint->get_model(mn);
1407  model->set_name(sp._b);
1408  }
1409  }
1410 
1411  } else {
1412  nout << "Couldn't find joint " << sp._a << "\n";
1413  }
1414  }
1415  }
1416 }
1417 
1418 ////////////////////////////////////////////////////////////////////
1419 // Function: EggOptchar::change_dart_type
1420 // Access: Private
1421 // Description: Recursively walks the indicated egg hierarchy,
1422 // renaming geometry to the indicated name.
1423 ////////////////////////////////////////////////////////////////////
1424 void EggOptchar::
1425 change_dart_type(EggGroupNode *egg_group, const string &new_dart_type) {
1426  EggGroupNode::iterator gi;
1427  for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
1428  EggNode *child = (*gi);
1429  if (child->is_of_type(EggGroupNode::get_class_type())) {
1430  EggGroupNode *group = DCAST(EggGroupNode, child);
1431  if (child->is_of_type(EggGroup::get_class_type())) {
1432  EggGroup *gr = DCAST(EggGroup, child);
1433  EggGroup::DartType dt = gr->get_dart_type();
1434  if(dt != EggGroup::DT_none) {
1435  EggGroup::DartType newDt = gr->string_dart_type(new_dart_type);
1436  gr->set_dart_type(newDt);
1437  }
1438  }
1439  change_dart_type(group, new_dart_type);
1440  }
1441  }
1442 }
1443 
1444 
1445 ////////////////////////////////////////////////////////////////////
1446 // Function: EggOptchar::rename_primitives
1447 // Access: Private
1448 // Description: Recursively walks the indicated egg hierarchy,
1449 // renaming geometry to the indicated name.
1450 ////////////////////////////////////////////////////////////////////
1451 void EggOptchar::
1452 rename_primitives(EggGroupNode *egg_group, const string &name) {
1453  EggGroupNode::iterator gi;
1454  for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
1455  EggNode *child = (*gi);
1456 
1457  if (child->is_of_type(EggGroupNode::get_class_type())) {
1458  EggGroupNode *group = DCAST(EggGroupNode, child);
1459  rename_primitives(group, name);
1460 
1461  } else if (child->is_of_type(EggPrimitive::get_class_type())) {
1462  child->set_name(name);
1463  }
1464  }
1465 }
1466 
1467 ////////////////////////////////////////////////////////////////////
1468 // Function: EggOptchar::do_preload
1469 // Access: Private
1470 // Description: Generates the preload tables for each model.
1471 ////////////////////////////////////////////////////////////////////
1472 void EggOptchar::
1473 do_preload() {
1474  // First, build up the list of AnimPreload entries, one for each
1475  // animation file.
1476  PT(EggGroup) anim_group = new EggGroup("preload");
1477 
1478  int num_characters = _collection->get_num_characters();
1479  int ci;
1480  for (ci = 0; ci < num_characters; ++ci) {
1481  EggCharacterData *char_data = _collection->get_character(ci);
1482 
1483  int num_models = char_data->get_num_models();
1484  for (int mn = 0; mn < num_models; ++mn) {
1485  EggNode *root = char_data->get_model_root(mn);
1486  if (root->is_of_type(EggTable::get_class_type())) {
1487  // This model represents an animation.
1488  EggData *data = char_data->get_egg_data(mn);
1489  string basename = data->get_egg_filename().get_basename_wo_extension();
1490  PT(EggAnimPreload) anim_preload = new EggAnimPreload(basename);
1491 
1492  int mi = char_data->get_model_index(mn);
1493  anim_preload->set_num_frames(char_data->get_num_frames(mi));
1494  double frame_rate = char_data->get_frame_rate(mi);
1495  if (frame_rate != 0.0) {
1496  anim_preload->set_fps(frame_rate);
1497  }
1498 
1499  anim_group->add_child(anim_preload);
1500  }
1501  }
1502  }
1503 
1504  // Now go back through and copy the preload tables into each of the
1505  // model files.
1506  for (ci = 0; ci < num_characters; ++ci) {
1507  EggCharacterData *char_data = _collection->get_character(ci);
1508 
1509  int num_models = char_data->get_num_models();
1510  for (int mn = 0; mn < num_models; ++mn) {
1511  EggNode *root = char_data->get_model_root(mn);
1512  if (root->is_of_type(EggGroup::get_class_type())) {
1513  // This is a model file. Copy in the table.
1514  EggGroup *model_root = DCAST(EggGroup, root);
1515  EggGroup::const_iterator ci;
1516  for (ci = anim_group->begin(); ci != anim_group->end(); ++ci) {
1517  EggAnimPreload *anim_preload = DCAST(EggAnimPreload, *ci);
1518  PT(EggAnimPreload) new_anim_preload = new EggAnimPreload(*anim_preload);
1519  model_root->add_child(new_anim_preload);
1520  }
1521  }
1522  }
1523  }
1524 }
1525 
1526 ////////////////////////////////////////////////////////////////////
1527 // Function: EggOptchar::do_defpose
1528 // Access: Private
1529 // Description: Sets the initial pose for the character(s).
1530 ////////////////////////////////////////////////////////////////////
1531 void EggOptchar::
1532 do_defpose() {
1533  // Split out the defpose parameter.
1534  Filename egg_filename;
1535  size_t comma = _defpose.find(',');
1536  egg_filename = _defpose.substr(0, comma);
1537 
1538  string frame_str;
1539  if (comma != string::npos) {
1540  frame_str = _defpose.substr(comma + 1);
1541  }
1542  frame_str = trim(frame_str);
1543  int frame = 0;
1544  if (!frame_str.empty()) {
1545  if (!string_to_int(frame_str, frame)) {
1546  nout << "Invalid integer in -defpose: " << frame_str << "\n";
1547  return;
1548  }
1549  }
1550 
1551  // Now find the named animation file in our egg list.
1552  int egg_index = -1;
1553  int num_eggs = _collection->get_num_eggs();
1554  int i;
1555 
1556  // First, look for an exact match.
1557  for (i = 0; i < num_eggs && egg_index == -1; ++i) {
1558  if (_collection->get_egg(i)->get_egg_filename() == egg_filename) {
1559  egg_index = i;
1560  }
1561  }
1562 
1563  // Then, look for an inexact match.
1564  string egg_basename = egg_filename.get_basename_wo_extension();
1565  for (i = 0; i < num_eggs && egg_index == -1; ++i) {
1566  if (_collection->get_egg(i)->get_egg_filename().get_basename_wo_extension() == egg_basename) {
1567  egg_index = i;
1568  }
1569  }
1570 
1571  if (egg_index == -1) {
1572  // No joy.
1573  nout << "Egg file " << egg_filename << " named in -defpose, but does not appear on command line.\n";
1574  return;
1575  }
1576 
1577  EggData *egg_data = _collection->get_egg(egg_index);
1578 
1579  if (_collection->get_num_models(egg_index) == 0) {
1580  nout << "Egg file " << egg_filename << " does not include any model or animation.\n";
1581  return;
1582  }
1583 
1584  // Now get the first model (or animation) named by this egg file.
1585  int mi = _collection->get_first_model_index(egg_index);
1586  EggCharacterData *ch = _collection->get_character_by_model_index(mi);
1587  EggJointData *root_joint = ch->get_root_joint();
1588 
1589  int anim_index = -1;
1590  for (i = 0; i < ch->get_num_models() && anim_index == -1; ++i) {
1591  if (ch->get_egg_data(i) == egg_data) {
1592  anim_index = i;
1593  }
1594  }
1595 
1596  // This couldn't possibly fail, since we already checked this above.
1597  nassertv(anim_index != -1);
1598 
1599  // Now we can recursively apply the default pose to the hierarchy.
1600  ch->get_root_joint()->apply_default_pose(anim_index, frame);
1601 }
1602 
1603 int main(int argc, char *argv[]) {
1604  // A call to pystub() to force libpystub.so to be linked in.
1605  pystub();
1606 
1607  EggOptchar prog;
1608  prog.parse_command_line(argc, argv);
1609  prog.run();
1610  return 0;
1611 }
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...
Definition: eggJointData.I:112
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
Definition: eggVertex.cxx:819
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...
Definition: eggVertex.cxx:805
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
int get_num_joints() const
Returns the total number of joints in the character joint hierarchy.
EggJointData * find_joint(const string &name) const
Returns the first joint found with the indicated name, or NULL if no joint has that name...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
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.
static DartType string_dart_type(const string &strval)
Returns the DartType value associated with the given string representation, or DT_none if the string ...
Definition: eggGroup.cxx:932
virtual void set_name(const string &name)
Applies the indicated name change to the egg file.
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_...
bool almost_equal(const LMatrix4d &other, double threshold) const
Returns true if two matrices are memberwise equal within a specified tolerance.
Definition: lmatrix.cxx:2058
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
void add_normals_options()
Adds -no, -np, etc.
Definition: eggBase.cxx:63
int get_num_models() const
Returns the maximum number of back pointers this component may have.
void zero_channels(const string &components)
Calls zero_channels() on all models for this joint, but does not recurse downwards.
const Filename & get_egg_filename() const
Returns the directory in which the egg file is considered to reside.
Definition: eggData.I:135
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...
Definition: eggVertex.cxx:789
This corresponds to a single morph slider control.
Definition: eggSliderData.h:31
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...
Definition: eggData.h:41
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.
virtual bool has_vertices() const
Returns true if there are any vertices referenced by the node this points to, false otherwise...
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
void quantize_channels(const string &components, double quantum)
Calls quantize_channels() on all models for this joint, and then recurses downwards to all joints bel...
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...
Definition: eggGroup.h:36
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This corresponds to an <AnimPreload> entry.
static const LMatrix4d & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:5168
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
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...
Definition: eggOptchar.h:41
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 ...
Definition: eggObject.cxx:102
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.
Definition: eggGroup.cxx:756
This class contains extra user data which is piggybacked onto EggGroup objects for the purpose of the...
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...
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
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...
void set_user_data(EggUserData *user_data)
Sets the user data associated with this object.
Definition: eggObject.cxx:89
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
Definition: eggJointData.h:34
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.
Definition: eggNode.h:38
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.
Definition: eggBase.cxx:135
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.
Definition: typedObject.I:63
double get_frame_rate(int model_index) const
Returns the stated frame rate of the specified model.
This stores a pointer from an EggJointData or EggSliderData object back to the referencing data in an...
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.
Definition: eggVertexPool.h:46
static bool is_compression_available()
Returns true if the FFTW library is compiled in, so that this class is actually capable of doing usef...
EggJointData * make_new_joint(const string &name, EggJointData *parent)
Creates a new joint as a child of the indicated joint and returns it.
This class can be used to test for string matches against standard Unix-shell filename globbing conve...
Definition: globPattern.h:37
int get_model_index(int n) const
Returns the model_index of the nth model associated with this character.