Panda3D
eggCharacterData.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 eggCharacterData.cxx
10  * @author drose
11  * @date 2001-02-23
12  */
13 
14 #include "eggCharacterData.h"
15 #include "eggCharacterCollection.h"
16 #include "eggCharacterDb.h"
17 #include "eggJointData.h"
18 #include "eggSliderData.h"
19 #include "indent.h"
20 
21 #include <algorithm>
22 
23 // An STL function object to sort the joint list in order from highest to
24 // lowest in the new hierarchy. Used in do_reparent().
25 class OrderJointsByNewDepth {
26 public:
27  bool operator()(const EggJointData *a, const EggJointData *b) const {
28  return a->_new_parent_depth < b->_new_parent_depth;
29  }
30 };
31 
32 
33 /**
34  *
35  */
36 EggCharacterData::
37 EggCharacterData(EggCharacterCollection *collection) :
38  _component_names("_", "joint_")
39 {
40  _collection = collection;
41  _root_joint = _collection->make_joint_data(this);
42  // The fictitious root joint is not added to the _components list.
43 }
44 
45 /**
46  *
47  */
48 EggCharacterData::
49 ~EggCharacterData() {
50  delete _root_joint;
51 
52  Sliders::iterator si;
53  for (si = _sliders.begin(); si != _sliders.end(); ++si) {
54  EggSliderData *slider = (*si);
55  delete slider;
56  }
57 }
58 
59 /**
60  * Renames all of the models in the character data to the indicated name.
61  * This is the name that is used to identify unique skeleton hierarchies; if
62  * you set two different models to the same name, they will be loaded together
63  * as if they are expected to have the same skeleton hierarchy.
64  */
66 rename_char(const std::string &name) {
67  Models::iterator mi;
68  for (mi = _models.begin(); mi != _models.end(); ++mi) {
69  (*mi)._model_root->set_name(name);
70  }
71 
72  set_name(name);
73 }
74 
75 /**
76  * Indicates that the given model_index (with the indicated model_root) is
77  * associated with this character. This is normally called by the
78  * EggCharacterCollection class as new models are discovered.
79  *
80  * A "model" here is either a character model (or one LOD of a character
81  * model), or a character animation file: in either case, a hierarchy of
82  * joints.
83  */
85 add_model(int model_index, EggNode *model_root, EggData *egg_data) {
86  Model m;
87  m._model_index = model_index;
88  m._model_root = model_root;
89  m._egg_data = egg_data;
90  _models.push_back(m);
91 }
92 
93 /**
94  * Returns the number of frames of animation of the indicated model. This is
95  * more reliable than asking a particular joint or slider of the animation for
96  * its number of frames, since a particular joint may have only 1 frame (if it
97  * is unanimated), even though the overall animation has many frames.
98  */
100 get_num_frames(int model_index) const {
101  int max_num_frames = 0;
102  Components::const_iterator ci;
103  for (ci = _components.begin(); ci != _components.end(); ++ci) {
104  EggComponentData *component = (*ci);
105  int num_frames = component->get_num_frames(model_index);
106  if (num_frames > 1) {
107  // We have a winner. Assume all other components will be similar.
108  return num_frames;
109  }
110  max_num_frames = std::max(max_num_frames, num_frames);
111  }
112 
113  // Every component had either 1 frame or 0 frames. Return the maximum of
114  // these.
115  return max_num_frames;
116 }
117 
118 /**
119  * Returns the stated frame rate of the specified model. Similar to
120  * get_num_frames().
121  */
122 double EggCharacterData::
123 get_frame_rate(int model_index) const {
124  Components::const_iterator ci;
125  for (ci = _components.begin(); ci != _components.end(); ++ci) {
126  EggComponentData *component = (*ci);
127  double frame_rate = component->get_frame_rate(model_index);
128  if (frame_rate != 0.0) {
129  // We have a winner. Assume all other components will be similar.
130  return frame_rate;
131  }
132  }
133 
134  return 0.0;
135 }
136 
137 /**
138  * Walks through each component and ensures that all have the same number of
139  * frames of animation (except for those that contain 0 or 1 frames, of
140  * course). Returns true if all are valid, false if there is a discreprency
141  * (in which case the shorter component are extended).
142  */
144 check_num_frames(int model_index) {
145  int max_num_frames = 0;
146  bool any_violations = false;
147  Components::const_iterator ci;
148  for (ci = _components.begin(); ci != _components.end(); ++ci) {
149  EggComponentData *component = (*ci);
150  int num_frames = component->get_num_frames(model_index);
151  if (num_frames > 1 && max_num_frames > 1 &&
152  max_num_frames != num_frames) {
153  // If we have two different opinions about the number of frames (other
154  // than 0 or 1), we have a discrepency. This is an error condition.
155  any_violations = true;
156  }
157  max_num_frames = std::max(max_num_frames, num_frames);
158  }
159 
160  if (any_violations) {
161  // Now go back through and force all components to the appropriate length.
162  for (ci = _components.begin(); ci != _components.end(); ++ci) {
163  EggComponentData *component = (*ci);
164  int num_frames = component->get_num_frames(model_index);
165  if (num_frames > 1 && max_num_frames != num_frames) {
166  component->extend_to(model_index, max_num_frames);
167  }
168  }
169  }
170 
171  return !any_violations;
172 }
173 
174 /**
175  * Begins the process of restructuring the joint hierarchy according to the
176  * previous calls to reparent_to() on various joints. This will reparent the
177  * joint hierachy in all models as requested, while adjusting the transforms
178  * as appropriate so that each joint retains the same net transform across all
179  * frames that it had before the operation. Returns true on success, false on
180  * failure.
181  */
184  typedef pset<EggJointData *> InvalidSet;
185  InvalidSet invalid_set;
186 
187  // To begin, make sure the list of new_children is accurate.
188  Joints::const_iterator ji;
189  for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
190  EggJointData *joint_data = (*ji);
191  joint_data->do_begin_reparent();
192  }
193  // We also need to clear the children on the root joint, but the root joint
194  // doesn't get any of the other operations (including finish_reparent)
195  // applied to it.
196  _root_joint->do_begin_reparent();
197 
198 
199  // Now, check for cycles in the new parenting hierarchy, and also sort the
200  // joints in order from top to bottom in the new hierarchy.
201  for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
202  EggJointData *joint_data = (*ji);
203  pset<EggJointData *> chain;
204  if (joint_data->calc_new_parent_depth(chain)) {
205  nout << "Cycle detected in parent chain for " << joint_data->get_name()
206  << "!\n";
207  return false;
208  }
209  }
210  sort(_joints.begin(), _joints.end(), OrderJointsByNewDepth());
211 
212  // Now compute the new transforms for the joints' new positions. This is
213  // done recursively through the new parent hierarchy, so we can take
214  // advantage of caching the net value for a particular frame.
215  Models::const_iterator mi;
216  for (mi = _models.begin(); mi != _models.end(); ++mi) {
217  EggCharacterDb db;
218  int model_index = (*mi)._model_index;
219  int num_frames = get_num_frames(model_index);
220  nout << " computing " << (mi - _models.begin()) + 1
221  << " of " << _models.size()
222  << ": " << (*mi)._egg_data->get_egg_filename()
223  << " (" << num_frames << " frames)\n";
224  for (int f = 0; f < num_frames; f++) {
225  // First, walk through all the joints and flush the computed net
226  // transforms from before.
227  for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
228  EggJointData *joint_data = (*ji);
229  joint_data->do_begin_compute_reparent();
230  }
231  _root_joint->do_begin_compute_reparent();
232 
233  // Now go back through and compute the reparented transforms, caching
234  // net transforms as necessary.
235  for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
236  EggJointData *joint_data = (*ji);
237  if (!joint_data->do_compute_reparent(model_index, f, db)) {
238  // Oops, we got an invalid transform.
239  invalid_set.insert(joint_data);
240  }
241  }
242  }
243 
244  // Finally, apply the computations to the joints.
245  for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
246  EggJointData *joint_data = (*ji);
247  if (!joint_data->do_joint_rebuild(model_index, db)) {
248  invalid_set.insert(joint_data);
249  }
250  }
251  }
252 
253  // Now remove all of the old children and add in the new children.
254  for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
255  EggJointData *joint_data = (*ji);
256  joint_data->do_finish_reparent();
257  }
258 
259  // Report the set of joints that failed. It really shouldn't be possible
260  // for any joints to fail, so if you see anything reported here, something
261  // went wrong at a fundamental level. Perhaps a problem with
262  // decompose_matrix().
263  InvalidSet::const_iterator si;
264  for (si = invalid_set.begin(); si != invalid_set.end(); ++si) {
265  EggJointData *joint_data = (*si);
266  // Don't bother reporting joints that no longer have a parent, since we
267  // don't care about joints that are now outside the hierarchy.
268  if (joint_data->get_parent() != nullptr) {
269  nout << "Warning: reparenting " << joint_data->get_name()
270  << " to ";
271  if (joint_data->get_parent() == _root_joint) {
272  nout << "the root";
273  } else {
274  nout << joint_data->get_parent()->get_name();
275  }
276  nout << " results in an invalid transform.\n";
277  }
278  }
279 
280  return invalid_set.empty();
281 }
282 
283 /**
284  * Chooses the best possible parent joint for each of the joints in the
285  * hierarchy, based on the score computed by
286  * EggJointData::score_reparent_to(). This is a fairly expensive operation
287  * that involves lots of recomputing of transforms across the hierarchy.
288  *
289  * The joints are not actually reparented yet, but the new_parent of each
290  * joint is set. Call do_reparent() to actually perform the suggested
291  * reparenting operation.
292  */
295  EggCharacterDb db;
296 
297  Joints::const_iterator ji, jj;
298  for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
299  EggJointData *joint_data = (*ji);
300 
301  EggJointData *best_parent = joint_data->get_parent();
302  int best_score = joint_data->score_reparent_to(best_parent, db);
303 
304  for (jj = _joints.begin(); jj != _joints.end(); ++jj) {
305  EggJointData *possible_parent = (*jj);
306  if (possible_parent != joint_data && possible_parent != best_parent &&
307  !joint_data->is_new_ancestor(possible_parent)) {
308 
309  int score = joint_data->score_reparent_to(possible_parent, db);
310  if (score >= 0 && (best_score < 0 || score < best_score)) {
311  best_parent = possible_parent;
312  best_score = score;
313  }
314  }
315  }
316 
317  // Also consider reparenting the node to the root.
318  EggJointData *possible_parent = get_root_joint();
319  if (possible_parent != best_parent) {
320  int score = joint_data->score_reparent_to(possible_parent, db);
321  if (score >= 0 && (best_score < 0 || score < best_score)) {
322  best_parent = possible_parent;
323  best_score = score;
324  }
325  }
326 
327  if (best_parent != nullptr &&
328  best_parent != joint_data->_parent) {
329  nout << "best parent for " << joint_data->get_name() << " is "
330  << best_parent->get_name() << "\n";
331  joint_data->reparent_to(best_parent);
332  }
333  }
334 }
335 
336 /**
337  * Returns the slider with the indicated name, or NULL if no slider has that
338  * name.
339  */
341 find_slider(const std::string &name) const {
342  SlidersByName::const_iterator si;
343  si = _sliders_by_name.find(name);
344  if (si != _sliders_by_name.end()) {
345  return (*si).second;
346  }
347 
348  return nullptr;
349 }
350 
351 /**
352  * Returns the slider matching the indicated name. If no such slider exists
353  * already, creates a new one.
354  */
356 make_slider(const std::string &name) {
357  SlidersByName::const_iterator si;
358  si = _sliders_by_name.find(name);
359  if (si != _sliders_by_name.end()) {
360  return (*si).second;
361  }
362 
363  EggSliderData *slider = _collection->make_slider_data(this);
364  slider->set_name(name);
365  _sliders_by_name.insert(SlidersByName::value_type(name, slider));
366  _sliders.push_back(slider);
367  _components.push_back(slider);
368  return slider;
369 }
370 
371 /**
372  * Returns the estimated amount of memory, in megabytes, that will be required
373  * to perform the do_reparent() operation. This is used mainly be
374  * EggCharacterDb to decide up front whether to store this data in-RAM or on-
375  * disk.
376  */
377 size_t EggCharacterData::
379  // Count how much memory we will need to store the interim transforms. This
380  // is models * joints * frames * 3 * sizeof(LMatrix4d).
381  size_t mj_frames = 0;
382  Models::const_iterator mi;
383  for (mi = _models.begin(); mi != _models.end(); ++mi) {
384  int model_index = (*mi)._model_index;
385  size_t num_frames = (size_t)get_num_frames(model_index);
386  mj_frames += num_frames * _joints.size();
387  }
388 
389  // We do this operation a bit carefully, to guard against integer overflow.
390  size_t mb_needed = ((mj_frames * 3 / 1024) * sizeof(LMatrix4d)) / 1024;
391 
392  return mb_needed;
393 }
394 
395 
396 /**
397  *
398  */
399 void EggCharacterData::
400 write(std::ostream &out, int indent_level) const {
401  indent(out, indent_level)
402  << "Character " << get_name() << ":\n";
403  get_root_joint()->write(out, indent_level + 2);
404 
405  Sliders::const_iterator si;
406  for (si = _sliders.begin(); si != _sliders.end(); ++si) {
407  EggSliderData *slider = (*si);
408  slider->write(out, indent_level + 2);
409  }
410 }
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
EggCharacterData::make_slider
EggSliderData * make_slider(const std::string &name)
Returns the slider matching the indicated name.
Definition: eggCharacterData.cxx:356
eggCharacterCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggCharacterDb.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggCharacterData::find_slider
EggSliderData * find_slider(const std::string &name) const
Returns the slider with the indicated name, or NULL if no slider has that name.
Definition: eggCharacterData.cxx:341
EggCharacterCollection::make_slider_data
virtual EggSliderData * make_slider_data(EggCharacterData *char_data)
Allocates and returns a new EggSliderData structure for the given character.
Definition: eggCharacterCollection.cxx:161
EggComponentData::get_frame_rate
double get_frame_rate(int model_index) const
Returns the number of frames of animation for this particular component in the indicated model.
Definition: eggComponentData.cxx:110
EggCharacterData::add_model
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...
Definition: eggCharacterData.cxx:85
EggCharacterData::choose_optimal_hierarchy
void choose_optimal_hierarchy()
Chooses the best possible parent joint for each of the joints in the hierarchy, based on the score co...
Definition: eggCharacterData.cxx:294
EggJointData::reparent_to
void reparent_to(EggJointData *new_parent)
Indicates an intention to change the parent of this joint to the indicated joint, or NULL to remove i...
Definition: eggJointData.I:91
eggJointData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggCharacterData::estimate_db_size
size_t estimate_db_size() const
Returns the estimated amount of memory, in megabytes, that will be required to perform the do_reparen...
Definition: eggCharacterData.cxx:378
EggCharacterCollection::make_joint_data
virtual EggJointData * make_joint_data(EggCharacterData *char_data)
Allocates and returns a new EggJointData structure for the given character.
Definition: eggCharacterCollection.cxx:150
EggCharacterData::get_frame_rate
double get_frame_rate(int model_index) const
Returns the stated frame rate of the specified model.
Definition: eggCharacterData.cxx:123
EggData
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
EggJointData
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
Definition: eggJointData.h:31
eggSliderData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggComponentData
This is the base class of both EggJointData and EggSliderData.
Definition: eggComponentData.h:34
EggCharacterData::get_num_frames
int get_num_frames(int model_index) const
Returns the number of frames of animation of the indicated model.
Definition: eggCharacterData.cxx:100
EggCharacterData::rename_char
void rename_char(const std::string &name)
Renames all of the models in the character data to the indicated name.
Definition: eggCharacterData.cxx:66
EggComponentData::get_num_frames
int get_num_frames(int model_index) const
Returns the number of frames of animation for this particular component in the indicated model.
Definition: eggComponentData.cxx:86
eggCharacterData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggSliderData
This corresponds to a single morph slider control.
Definition: eggSliderData.h:28
EggJointData::score_reparent_to
int score_reparent_to(EggJointData *new_parent, EggCharacterDb &db)
Computes a score >= 0 reflecting the similarity of the current joint's animation (in world space) to ...
Definition: eggJointData.cxx:174
EggCharacterData::check_num_frames
bool check_num_frames(int model_index)
Walks through each component and ensures that all have the same number of frames of animation (except...
Definition: eggCharacterData.cxx:144
EggCharacterData::get_root_joint
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
Definition: eggCharacterData.I:71
EggCharacterData::do_reparent
bool do_reparent()
Begins the process of restructuring the joint hierarchy according to the previous calls to reparent_t...
Definition: eggCharacterData.cxx:183
EggComponentData::extend_to
void extend_to(int model_index, int num_frames) const
Extends the number of frames in the indicated model (presumably an animation table model) to the give...
Definition: eggComponentData.cxx:99
EggCharacterCollection
Represents a set of characters, as read and collected from possibly several model and/or animation eg...
Definition: eggCharacterCollection.h:32
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
EggCharacterDb
This class is used during joint optimization or restructuring to store the table of interim joint com...
Definition: eggCharacterDb.h:41
pset
This is our own Panda specialization on the default STL set.
Definition: pset.h:49