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