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