Panda3D
eggCharacterCollection.cxx
1 // Filename: eggCharacterCollection.cxx
2 // Created by: drose (26Feb01)
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 "eggCharacterCollection.h"
16 #include "eggCharacterData.h"
17 #include "eggJointData.h"
18 #include "eggSliderData.h"
19 
20 #include "dcast.h"
21 #include "eggGroup.h"
22 #include "eggTable.h"
23 #include "eggPrimitive.h"
24 #include "eggVertex.h"
25 #include "eggVertexUV.h"
26 #include "eggMorphList.h"
27 #include "eggSAnimData.h"
28 #include "indirectCompareNames.h"
29 #include "indent.h"
30 
31 #include <algorithm>
32 
33 
34 ////////////////////////////////////////////////////////////////////
35 // Function: EggCharacterCollection::Constructor
36 // Access: Public
37 // Description:
38 ////////////////////////////////////////////////////////////////////
39 EggCharacterCollection::
40 EggCharacterCollection() {
41  _next_model_index = 0;
42 }
43 
44 ////////////////////////////////////////////////////////////////////
45 // Function: EggCharacterCollection::Destructor
46 // Access: Public, Virtual
47 // Description:
48 ////////////////////////////////////////////////////////////////////
49 EggCharacterCollection::
50 ~EggCharacterCollection() {
51  Characters::iterator ci;
52 
53  for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
54  delete (*ci);
55  }
56 }
57 
58 ////////////////////////////////////////////////////////////////////
59 // Function: EggCharacterCollection::add_egg
60 // Access: Public
61 // Description: Adds a new egg file to the list of models and
62 // animation files for this particular character.
63 //
64 // Returns the new egg_index if the file is successfully
65 // added, or -1 if there is some problem (for instance,
66 // it does not contain a character model or animation
67 // table).
68 //
69 // If the joint hierarchy does not match the existing
70 // joint hierarchy, a best match is attempted.
71 ////////////////////////////////////////////////////////////////////
74  _top_egg_nodes.clear();
75 
76  if (!scan_hierarchy(egg)) {
77  return -1;
78  }
79 
80  int egg_index = _eggs.size();
81  _eggs.push_back(EggInfo());
82  EggInfo &egg_info = _eggs.back();
83  egg_info._egg = egg;
84  egg_info._first_model_index = 0;
85 
86  // Now, for each model, add an entry in the egg_info and match the
87  // joint hierarchy to the known joints.
88  TopEggNodesByName::iterator tni;
89  for (tni = _top_egg_nodes.begin(); tni != _top_egg_nodes.end(); ++tni) {
90  string character_name = (*tni).first;
91  TopEggNodes &top_nodes = (*tni).second;
92  EggCharacterData *char_data = make_character(character_name);
93  EggJointData *root_joint = char_data->get_root_joint();
94 
95  TopEggNodes::iterator ti;
96  for (ti = top_nodes.begin(); ti != top_nodes.end(); ++ti) {
97  EggNode *model_root = (*ti).first;
98  ModelDescription &desc = (*ti).second;
99 
100  int model_index = _next_model_index++;
101  if (egg_info._models.empty()) {
102  egg_info._first_model_index = model_index;
103  }
104  egg_info._models.push_back(model_root);
105 
106  char_data->add_model(model_index, model_root, egg);
107  nassertr(model_index == (int)_characters_by_model_index.size(), -1);
108  _characters_by_model_index.push_back(char_data);
109  root_joint->add_back_pointer(model_index, desc._root_node);
110 
111  match_egg_nodes(char_data, root_joint, desc._top_nodes,
112  egg_index, model_index);
113 
114  scan_for_morphs(model_root, model_index, char_data);
115  scan_for_sliders(model_root, model_index, char_data);
116  }
117  }
118 
119  return egg_index;
120 }
121 
122 ////////////////////////////////////////////////////////////////////
123 // Function: EggCharacterCollection::get_character_by_name
124 // Access: Public
125 // Description: Returns the Character with the indicated name, if it
126 // exists in the collection, or NULL if it does not.
127 ////////////////////////////////////////////////////////////////////
129 get_character_by_name(const string &character_name) const {
130  Characters::const_iterator ci;
131  for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
132  EggCharacterData *char_data = (*ci);
133  if (char_data->get_name() == character_name) {
134  return char_data;
135  }
136  }
137 
138  return (EggCharacterData *)NULL;
139 }
140 
141 
142 ////////////////////////////////////////////////////////////////////
143 // Function: EggCharacterCollection::make_character_data
144 // Access: Public, Virtual
145 // Description: Allocates and returns a new EggCharacterData
146 // structure. This is primarily intended as a hook so
147 // derived classes can customize the type of
148 // EggCharacterData nodes used to represent the
149 // characters in this collection.
150 ////////////////////////////////////////////////////////////////////
153  return new EggCharacterData(this);
154 }
155 
156 ////////////////////////////////////////////////////////////////////
157 // Function: EggCharacterCollection::make_joint_data
158 // Access: Public, Virtual
159 // Description: Allocates and returns a new EggJointData structure
160 // for the given character. This is primarily intended
161 // as a hook so derived classes can customize the type
162 // of EggJointData nodes used to represent the joint
163 // hierarchy.
164 ////////////////////////////////////////////////////////////////////
167  return new EggJointData(this, char_data);
168 }
169 
170 ////////////////////////////////////////////////////////////////////
171 // Function: EggCharacterCollection::make_slider_data
172 // Access: Public, Virtual
173 // Description: Allocates and returns a new EggSliderData structure
174 // for the given character. This is primarily intended
175 // as a hook so derived classes can customize the type
176 // of EggSliderData nodes used to represent the slider
177 // list.
178 ////////////////////////////////////////////////////////////////////
181  return new EggSliderData(this, char_data);
182 }
183 
184 ////////////////////////////////////////////////////////////////////
185 // Function: EggCharacterCollection::make_character
186 // Access: Protected
187 // Description: Allocates and returns a new EggCharacterData object
188 // representing the named character, if there is not
189 // already a character by that name.
190 ////////////////////////////////////////////////////////////////////
192 make_character(const string &character_name) {
193  // Does the named character exist yet?
194 
195  Characters::iterator ci;
196  for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
197  EggCharacterData *char_data = (*ci);
198  if (char_data->get_name() == character_name) {
199  return char_data;
200  }
201  }
202 
203  // Define a new character.
204  EggCharacterData *char_data = make_character_data();
205  char_data->set_name(character_name);
206  _characters.push_back(char_data);
207  return char_data;
208 }
209 
210 ////////////////////////////////////////////////////////////////////
211 // Function: EggCharacterCollection::scan_hierarchy
212 // Access: Private
213 // Description: Walks the given egg data's hierarchy, looking for
214 // either the start of an animation channel or the start
215 // of a character model. Returns true if either (or
216 // both) is found, false if the model appears to have
217 // nothing to do with characters.
218 //
219 // Fills up the _top_egg_nodes according to the nodes
220 // found.
221 ////////////////////////////////////////////////////////////////////
222 bool EggCharacterCollection::
223 scan_hierarchy(EggNode *egg_node) {
224  if (egg_node->is_of_type(EggGroup::get_class_type())) {
225  EggGroup *group = DCAST(EggGroup, egg_node);
226  if (group->get_dart_type() != EggGroup::DT_none) {
227  // A group with a <Dart> flag begins a character model.
228  scan_for_top_joints(group, group, group->get_name());
229  return true;
230  }
231 
232  } else if (egg_node->is_of_type(EggTable::get_class_type())) {
233  EggTable *table = DCAST(EggTable, egg_node);
234  if (table->get_table_type() == EggTable::TT_bundle) {
235  // A <Bundle> begins an animation table.
236  scan_for_top_tables(table, table, table->get_name());
237  return true;
238  }
239  }
240 
241  bool character_found = false;
242  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
243  EggGroupNode *group = DCAST(EggGroupNode, egg_node);
244  EggGroupNode::iterator gi;
245  for (gi = group->begin(); gi != group->end(); ++gi) {
246  if (scan_hierarchy(*gi)) {
247  character_found = true;
248  }
249  }
250  }
251 
252  return character_found;
253 }
254 
255 ////////////////////////////////////////////////////////////////////
256 // Function: EggCharacterCollection::scan_for_top_joints
257 // Access: Private
258 // Description: Once a character model has been found, continue
259 // scanning the egg hierarchy to look for the topmost
260 // <Joint> nodes encountered.
261 ////////////////////////////////////////////////////////////////////
262 void EggCharacterCollection::
263 scan_for_top_joints(EggNode *egg_node, EggNode *model_root,
264  const string &character_name) {
265  if (egg_node->is_of_type(EggGroup::get_class_type())) {
266  EggGroup *group = DCAST(EggGroup, egg_node);
267 
268  if (group->has_lod()) {
269  // This group has an LOD specification; that indicates multiple
270  // skeleton hierarchies for this character, one for each LOD.
271  // We call each of these a separate model.
272  model_root = group;
273  }
274  if (group->get_group_type() == EggGroup::GT_joint) {
275  // A <Joint> node begins a model hierarchy.
276  ModelDescription &desc = _top_egg_nodes[character_name][model_root];
277  desc._root_node = model_root;
278  desc._top_nodes.push_back(group);
279  return;
280  }
281  }
282 
283  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
284  EggGroupNode *group = DCAST(EggGroupNode, egg_node);
285  EggGroupNode::iterator gi;
286  for (gi = group->begin(); gi != group->end(); ++gi) {
287  scan_for_top_joints(*gi, model_root, character_name);
288  }
289  }
290 }
291 
292 ////////////////////////////////////////////////////////////////////
293 // Function: EggCharacterCollection::scan_for_top_tables
294 // Access: Private
295 // Description: Once an animation has been found, continue scanning
296 // the egg hierarchy to look for the topmost <Table>
297 // nodes encountered.
298 ////////////////////////////////////////////////////////////////////
299 void EggCharacterCollection::
300 scan_for_top_tables(EggTable *bundle, EggNode *model_root,
301  const string &character_name) {
302  // We really only need to check the immediate children of the bundle
303  // for a table node called "<skeleton>".
304  EggGroupNode::iterator gi;
305  for (gi = bundle->begin(); gi != bundle->end(); ++gi) {
306  EggNode *child = (*gi);
307  if (child->is_of_type(EggTable::get_class_type())) {
308  EggTable *table = DCAST(EggTable, child);
309  if (table->get_name() == "<skeleton>") {
310  // Here it is! Now the immediate children of this node are
311  // the top tables.
312  ModelDescription &desc = _top_egg_nodes[character_name][model_root];
313  desc._root_node = table;
314 
315  EggGroupNode::iterator cgi;
316  for (cgi = table->begin(); cgi != table->end(); ++cgi) {
317  EggNode *grandchild = (*cgi);
318  if (grandchild->is_of_type(EggTable::get_class_type())) {
319  desc._top_nodes.push_back(grandchild);
320  }
321  }
322  }
323  }
324  }
325 }
326 
327 ////////////////////////////////////////////////////////////////////
328 // Function: EggCharacterCollection::scan_for_morphs
329 // Access: Private
330 // Description: Go back through a model's hierarchy and look for
331 // morph targets on the vertices and primitives.
332 ////////////////////////////////////////////////////////////////////
333 void EggCharacterCollection::
334 scan_for_morphs(EggNode *egg_node, int model_index,
335  EggCharacterData *char_data) {
336  if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
337  EggPrimitive *prim = DCAST(EggPrimitive, egg_node);
338  // Check for morphs on the primitive.
339  add_morph_back_pointers(prim, prim, model_index, char_data);
340 
341  // Also check for morphs on each of the prim's vertices.
342  EggPrimitive::const_iterator vi;
343  for (vi = prim->begin(); vi != prim->end(); ++vi) {
344  EggVertex *vertex = (*vi);
345 
346  add_morph_back_pointers(vertex, vertex, model_index, char_data);
347  add_morph_back_pointers_vertex(vertex, vertex, model_index, char_data);
348 
349  EggMorphVertexList::const_iterator mvi;
350  for (mvi = vertex->_dxyzs.begin();
351  mvi != vertex->_dxyzs.end();
352  ++mvi) {
353  const EggMorphVertex &morph = (*mvi);
354  char_data->make_slider(morph.get_name())->add_back_pointer(model_index, vertex);
355  }
356  }
357  }
358 
359  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
360  EggGroupNode *group = DCAST(EggGroupNode, egg_node);
361  EggGroupNode::iterator gi;
362  for (gi = group->begin(); gi != group->end(); ++gi) {
363  scan_for_morphs(*gi, model_index, char_data);
364  }
365  }
366 }
367 
368 ////////////////////////////////////////////////////////////////////
369 // Function: EggCharacterCollection::scan_for_sliders
370 // Access: Private
371 // Description: Go back to the animation tables and look for morph
372 // slider animation channels.
373 ////////////////////////////////////////////////////////////////////
374 void EggCharacterCollection::
375 scan_for_sliders(EggNode *egg_node, int model_index,
376  EggCharacterData *char_data) {
377  if (egg_node->is_of_type(EggTable::get_class_type())) {
378  EggTable *bundle = DCAST(EggTable, egg_node);
379 
380  // We really only need to check the immediate children of the
381  // bundle for a table node called "morph". This is a sibling of
382  // "<skeleton>", which we found a minute ago, but we weren't ready
383  // to scan for the morph sliders at the time, so we have to look
384  // again now.
385 
386  EggGroupNode::iterator gi;
387  for (gi = bundle->begin(); gi != bundle->end(); ++gi) {
388  EggNode *child = (*gi);
389  if (child->is_of_type(EggTable::get_class_type())) {
390  EggTable *table = DCAST(EggTable, child);
391  if (table->get_name() == "morph") {
392  // Here it is! Now the immediate children of this node are
393  // all the slider channels.
394 
395  EggGroupNode::iterator cgi;
396  for (cgi = table->begin(); cgi != table->end(); ++cgi) {
397  EggNode *grandchild = (*cgi);
398  if (grandchild->is_of_type(EggSAnimData::get_class_type())) {
399  char_data->make_slider(grandchild->get_name())->add_back_pointer(model_index, grandchild);
400  }
401  }
402  }
403  }
404  }
405  }
406 }
407 
408 ////////////////////////////////////////////////////////////////////
409 // Function: EggCharacterCollection::add_morph_back_pointers
410 // Access: Private
411 // Description: Adds the back pointers for the kinds of morphs we
412 // might find in an EggAttributes object.
413 ////////////////////////////////////////////////////////////////////
414 void EggCharacterCollection::
415 add_morph_back_pointers(EggAttributes *attrib, EggObject *egg_object,
416  int model_index, EggCharacterData *char_data) {
417  EggMorphNormalList::const_iterator mni;
418  for (mni = attrib->_dnormals.begin();
419  mni != attrib->_dnormals.end();
420  ++mni) {
421  const EggMorphNormal &morph = (*mni);
422  char_data->make_slider(morph.get_name())->add_back_pointer(model_index, egg_object);
423  }
424 
425  EggMorphColorList::const_iterator mci;
426  for (mci = attrib->_drgbas.begin();
427  mci != attrib->_drgbas.end();
428  ++mci) {
429  const EggMorphColor &morph = (*mci);
430  char_data->make_slider(morph.get_name())->add_back_pointer(model_index, egg_object);
431  }
432 }
433 
434 ////////////////////////////////////////////////////////////////////
435 // Function: EggCharacterCollection::add_morph_back_pointers_vertex
436 // Access: Private
437 // Description: Adds the back pointers for the kinds of morphs we
438 // might find in an EggVertex object.
439 ////////////////////////////////////////////////////////////////////
440 void EggCharacterCollection::
441 add_morph_back_pointers_vertex(EggVertex *vertex, EggObject *egg_object,
442  int model_index, EggCharacterData *char_data) {
444  for (ui = vertex->uv_begin(); ui != vertex->uv_end(); ++ui) {
445  EggVertexUV *vert_uv = (*ui);
446  EggMorphTexCoordList::const_iterator mti;
447  for (mti = vert_uv->_duvs.begin();
448  mti != vert_uv->_duvs.end();
449  ++mti) {
450  const EggMorphTexCoord &morph = (*mti);
451  char_data->make_slider(morph.get_name())->add_back_pointer(model_index, egg_object);
452  }
453  }
454 }
455 
456 ////////////////////////////////////////////////////////////////////
457 // Function: EggCharacterCollection::match_egg_nodes
458 // Access: Private
459 // Description: Attempts to match up the indicated list of egg_nodes
460 // with the children of the given joint_data, by name if
461 // possible.
462 //
463 // Also recurses on each matched joint to build up the
464 // entire joint hierarchy.
465 ////////////////////////////////////////////////////////////////////
466 void EggCharacterCollection::
467 match_egg_nodes(EggCharacterData *char_data, EggJointData *joint_data,
468  EggNodeList &egg_nodes, int egg_index, int model_index) {
469  // Sort the list of egg_nodes in order by name. This will make the
470  // matching up by names easier and more reliable.
471  sort(egg_nodes.begin(), egg_nodes.end(), IndirectCompareNames<Namable>());
472 
473  if (joint_data->_children.empty()) {
474  // If the EggJointData has no children yet, we must be the first.
475  // Gleefully define all the joints.
476  EggNodeList::iterator ei;
477  for (ei = egg_nodes.begin(); ei != egg_nodes.end(); ++ei) {
478  EggNode *egg_node = (*ei);
479  EggJointData *data = make_joint_data(char_data);
480  joint_data->_children.push_back(data);
481  char_data->_joints.push_back(data);
482  char_data->_components.push_back(data);
483  data->_parent = joint_data;
484  data->_new_parent = joint_data;
485  found_egg_match(char_data, data, egg_node, egg_index, model_index);
486  }
487 
488  } else {
489  // The EggJointData already has children; therefore, we have to
490  // match our joints up with the already-existing ones.
491 
492  EggNodeList extra_egg_nodes;
493  EggJointData::Children extra_data;
494 
495  EggNodeList::iterator ei;
496  EggJointData::Children::iterator di;
497 
498  ei = egg_nodes.begin();
499  di = joint_data->_children.begin();
500 
501  while (ei != egg_nodes.end() && di != joint_data->_children.end()) {
502  EggNode *egg_node = (*ei);
503  EggJointData *data = (*di);
504 
505  if (egg_node->get_name() < data->get_name()) {
506  // Here's a joint in the egg file, unmatched in the data.
507  extra_egg_nodes.push_back(egg_node);
508  ++ei;
509 
510  } else if (data->get_name() < egg_node->get_name()) {
511  // Here's a joint in the data, umatched by the egg file.
512  extra_data.push_back(data);
513  ++di;
514 
515  } else {
516  // Hey, these two match! Hooray!
517  found_egg_match(char_data, data, egg_node, egg_index, model_index);
518  ++ei;
519  ++di;
520  }
521  }
522 
523  while (ei != egg_nodes.end()) {
524  EggNode *egg_node = (*ei);
525 
526  // Here's a joint in the egg file, unmatched in the data.
527  extra_egg_nodes.push_back(egg_node);
528  ++ei;
529  }
530 
531  while (di != joint_data->_children.end()) {
532  EggJointData *data = (*di);
533 
534  // Here's a joint in the data, umatched by the egg file.
535  extra_data.push_back(data);
536  ++di;
537  }
538 
539  if (!extra_egg_nodes.empty()) {
540  // If we have some extra egg_nodes, we have to find a place to
541  // match them. (If we only had extra data, we don't care.)
542 
543  // First, check to see if any of the names match any past-used
544  // name.
545  EggNodeList more_egg_nodes;
546 
547  for (ei = extra_egg_nodes.begin(); ei != extra_egg_nodes.end(); ++ei) {
548  EggNode *egg_node = (*ei);
549  bool matched = false;
550  for (di = extra_data.begin(); di != extra_data.end(); ++di) {
551  EggJointData *data = (*di);
552  if (data->matches_name(egg_node->get_name())) {
553  found_egg_match(char_data, data, egg_node, egg_index, model_index);
554  extra_data.erase(di);
555  matched = true;
556  break;
557  }
558  }
559 
560  if (!matched) {
561  // This joint name was never seen before.
562  more_egg_nodes.push_back(egg_node);
563  }
564  }
565  extra_egg_nodes.swap(more_egg_nodes);
566  }
567 
568  if (!extra_egg_nodes.empty()) {
569  // Ok, we've still got to find a home for these remaining
570  // egg_nodes.
571  if (extra_egg_nodes.size() == extra_data.size()) {
572  // Match 'em up one-for-one.
573  size_t i;
574  for (i = 0; i < extra_egg_nodes.size(); i++) {
575  EggNode *egg_node = extra_egg_nodes[i];
576  EggJointData *data = extra_data[i];
577  found_egg_match(char_data, data, egg_node, egg_index, model_index);
578  }
579 
580  } else {
581  // Just tack 'em on the end.
582  EggNodeList::iterator ei;
583  for (ei = extra_egg_nodes.begin(); ei != extra_egg_nodes.end(); ++ei) {
584  EggNode *egg_node = (*ei);
585  EggJointData *data = make_joint_data(char_data);
586  joint_data->_children.push_back(data);
587  char_data->_joints.push_back(data);
588  char_data->_components.push_back(data);
589  data->_parent = joint_data;
590  data->_new_parent = joint_data;
591  found_egg_match(char_data, data, egg_node, egg_index, model_index);
592  }
593  }
594  }
595  }
596 
597  // Now sort the generated joint data hierarchy by name, just to be
598  // sure.
599  sort(joint_data->_children.begin(), joint_data->_children.end(),
601 }
602 
603 ////////////////////////////////////////////////////////////////////
604 // Function: EggCharacterCollection::found_egg_match
605 // Access: Private
606 // Description: Marks a one-to-one association between the indicated
607 // EggJointData and the indicated EggNode, and then
608 // recurses below.
609 ////////////////////////////////////////////////////////////////////
610 void EggCharacterCollection::
611 found_egg_match(EggCharacterData *char_data, EggJointData *joint_data,
612  EggNode *egg_node, int egg_index, int model_index) {
613  if (egg_node->has_name()) {
614  joint_data->add_name(egg_node->get_name(), char_data->_component_names);
615  }
616  egg_node->set_name(joint_data->get_name());
617  joint_data->add_back_pointer(model_index, egg_node);
618 
619  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
620  EggGroupNode *group_node = DCAST(EggGroupNode, egg_node);
621 
622  // Now consider all the children of egg_node that are themselves
623  // joints or tables.
624  EggNodeList egg_nodes;
625 
626  // Two approaches: either we are scanning a model with joints, or
627  // an animation bundle with tables.
628 
629  if (egg_node->is_of_type(EggGroup::get_class_type())) {
630  // A model with joints.
631  EggGroupNode::iterator gi;
632  for (gi = group_node->begin(); gi != group_node->end(); ++gi) {
633  EggNode *child = (*gi);
634  if (child->is_of_type(EggGroup::get_class_type())) {
635  EggGroup *group = DCAST(EggGroup, child);
636  if (group->get_group_type() == EggGroup::GT_joint) {
637  egg_nodes.push_back(group);
638  }
639  }
640  }
641 
642  } else {
643  // An animation bundle with tables.
644  EggGroupNode::iterator gi;
645  for (gi = group_node->begin(); gi != group_node->end(); ++gi) {
646  EggNode *child = (*gi);
647  if (child->is_of_type(EggTable::get_class_type())) {
648  EggTable *table = DCAST(EggTable, child);
649  if (!(table->get_name() == "xform")) {
650  egg_nodes.push_back(table);
651  }
652  }
653  }
654  }
655 
656  if (!egg_nodes.empty()) {
657  match_egg_nodes(char_data, joint_data, egg_nodes,
658  egg_index, model_index);
659  }
660  }
661 }
662 
663 ////////////////////////////////////////////////////////////////////
664 // Function: EggCharacterCollection::rename_char
665 // Access: Public
666 // Description: Renames the ith character to the indicated name.
667 // This name must not already be used by another
668 // character in the collection.
669 ////////////////////////////////////////////////////////////////////
671 rename_char(int i, const string &name) {
672  nassertv(i >= 0 && i < (int)_characters.size());
673 
674  EggCharacterData *char_data = _characters[i];
675  if (char_data->get_name() != name) {
676  nassertv(get_character_by_name(name) == (EggCharacterData *)NULL);
677  char_data->rename_char(name);
678  }
679 }
680 
681 ////////////////////////////////////////////////////////////////////
682 // Function: EggCharacterCollection::write
683 // Access: Public, Virtual
684 // Description:
685 ////////////////////////////////////////////////////////////////////
686 void EggCharacterCollection::
687 write(ostream &out, int indent_level) const {
688  Characters::const_iterator ci;
689 
690  for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
691  EggCharacterData *char_data = (*ci);
692  char_data->write(out, indent_level);
693  }
694 }
695 
696 ////////////////////////////////////////////////////////////////////
697 // Function: EggCharacterCollection::check_errors
698 // Access: Public
699 // Description: Can be called after the collection has been
700 // completely filled up with egg files to output any
701 // messages from warning conditions that have been
702 // detected, such as inconsistent animation tables.
703 //
704 // In addition to reporting this errors, calling this
705 // function will also ensure that they are all repaired.
706 // Pass force_initial_rest_frame as true to also force
707 // rest frames from different models to be the same if
708 // they are initially different.
709 ////////////////////////////////////////////////////////////////////
711 check_errors(ostream &out, bool force_initial_rest_frame) {
712  Characters::const_iterator ci;
713  for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
714  EggCharacterData *char_data = (*ci);
715  int num_joints = char_data->get_num_joints();
716  for (int j = 0; j < num_joints; j++) {
717  EggJointData *joint_data = char_data->get_joint(j);
718  if (joint_data->rest_frames_differ()) {
719  if (force_initial_rest_frame) {
720  joint_data->force_initial_rest_frame();
721  out << "Forced rest frames the same for " << joint_data->get_name()
722  << ".\n";
723  } else {
724  out << "Warning: rest frames for " << joint_data->get_name()
725  << " differ.\n";
726  }
727  }
728  }
729 
730  int num_models = char_data->get_num_models();
731  for (int mi = 0; mi < num_models; mi++) {
732  int model_index = char_data->get_model_index(mi);
733  if (!char_data->check_num_frames(model_index)) {
734  out << "Warning: animation from "
735  << char_data->get_egg_data(model_index)->get_egg_filename().get_basename()
736  << " had an inconsistent number of frames.\n";
737  }
738  }
739  }
740 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights...
Definition: eggPrimitive.h:51
The set of UV&#39;s that may or may not be assigned to a vertex.
Definition: eggVertexUV.h:32
string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:436
virtual EggCharacterData * make_character_data()
Allocates and returns a new EggCharacterData structure.
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
EggSliderData * make_slider(const string &name)
Returns the slider matching the indicated name.
int get_num_joints() const
Returns the total number of joints in the character joint hierarchy.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
bool check_num_frames(int model_index)
Walks through each component and ensures that all have the same number of frames of animation (except...
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
const Filename & get_egg_filename() const
Returns the directory in which the egg file is considered to reside.
Definition: eggData.I:135
This corresponds to a single morph slider control.
Definition: eggSliderData.h:31
int add_egg(EggData *egg)
Adds a new egg file to the list of models and animation files for this particular character...
int get_num_models() const
Returns the total number of models associated with this character.
A single <Dxyz> or <Duv> or some such entry.
Definition: eggMorph.h:33
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
bool rest_frames_differ() const
Returns true if the rest frames for different models differ in their initial value.
Definition: eggJointData.I:85
EggData * get_egg_data(int n) const
Returns the EggData representing the egg file that defined this particular model. ...
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:36
The set of attributes that may be applied to vertices as well as polygons, such as surface normal and...
Definition: eggAttributes.h:37
EggCharacterData * make_character(const string &character_name)
Allocates and returns a new EggCharacterData object representing the named character, if there is not already a character by that name.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
bool matches_name(const string &name) const
Returns true if the indicated name matches any name that was ever matched with this particular joint...
EggCharacterData * get_character_by_name(const string &character_name) const
Returns the Character with the indicated name, if it exists in the collection, or NULL if it does not...
Represents a single character, as read and collected from several models and animation files...
This corresponds to a.
Definition: eggTable.h:31
void rename_char(int i, const string &name)
Renames the ith character to the indicated name.
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
Definition: eggJointData.h:34
virtual void add_back_pointer(int model_index, EggObject *egg_object)
Adds the indicated model joint or anim table to the data.
virtual EggSliderData * make_slider_data(EggCharacterData *char_data)
Allocates and returns a new EggSliderData structure for the given character.
EggJointData * get_joint(int n) const
Returns the nth joint in the character joint hierarchy.
const_uv_iterator uv_begin() const
Returns an iterator that allows walking through the complete set of named UV&#39;s on the vertex...
Definition: eggVertex.I:273
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
void force_initial_rest_frame()
Forces all of the joints to have the same rest frame value as the first joint read in...
void add_name(const string &name, NameUniquifier &uniquifier)
Adds the indicated name to the set of names that this component can be identified with...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
void add_model(int model_index, EggNode *model_root, EggData *egg_data)
Indicates that the given model_index (with the indicated model_root) is associated with this characte...
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:75
void check_errors(ostream &out, bool force_initial_rest_frame)
Can be called after the collection has been completely filled up with egg files to output any message...
The highest-level base class in the egg directory.
Definition: eggObject.h:31
virtual EggJointData * make_joint_data(EggCharacterData *char_data)
Allocates and returns a new EggJointData structure for the given character.
const_uv_iterator uv_end() const
Returns an iterator that allows walking through the complete set of named UV&#39;s on the vertex...
Definition: eggVertex.I:301
int get_model_index(int n) const
Returns the model_index of the nth model associated with this character.