Panda3D
 All Classes Functions Variables Enumerations
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
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.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
bool rest_frames_differ() const
Returns true if the rest frames for different models differ in their initial value.
Definition: eggJointData.I:85
int get_num_models() const
Returns the total number of models associated with this character.
bool check_num_frames(int model_index)
Walks through each component and ensures that all have the same number of frames of animation (except...
bool matches_name(const string &name) const
Returns true if the indicated name matches any name that was ever matched with this particular joint...
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
int get_num_joints() const
Returns the total number of joints in the character joint hierarchy.
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...
A single &lt;Dxyz&gt; or &lt;Duv&gt; 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 has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:75
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
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
The main glue of the egg hierarchy, this corresponds to the &lt;Group&gt;, &lt;Instance&gt;, and &lt;Joint&gt; 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
int get_model_index(int n) const
Returns the model_index of the nth model associated with this character.
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.
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
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...
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
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
string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:436
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.
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...
EggJointData * get_joint(int n) const
Returns the nth joint in the character joint hierarchy.
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...
const Filename & get_egg_filename() const
Returns the directory in which the egg file is considered to reside.
Definition: eggData.I:135
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
EggData * get_egg_data(int n) const
Returns the EggData representing the egg file that defined this particular model. ...
virtual EggJointData * make_joint_data(EggCharacterData *char_data)
Allocates and returns a new EggJointData structure for the given character.