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 }
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_num_frames(int model_index) const
Returns the number of frames of animation for this particular component in the indicated model.
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
bool do_reparent()
Begins the process of restructuring the joint hierarchy according to the previous calls to reparent_t...
int get_num_frames(int model_index) const
Returns the number of frames of animation of the indicated model.
bool check_num_frames(int model_index)
Walks through each component and ensures that all have the same number of frames of animation (except...
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This corresponds to a single morph slider control.
Definition: eggSliderData.h:28
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
This is the base class of both EggJointData and EggSliderData.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is used during joint optimization or restructuring to store the table of interim joint com...
EggSliderData * find_slider(const std::string &name) const
Returns the slider with the indicated name, or NULL if no slider has that name.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Represents a set of characters, as read and collected from possibly several model and/or animation eg...
void choose_optimal_hierarchy()
Chooses the best possible parent joint for each of the joints in the hierarchy, based on the score co...
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 ...
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
double get_frame_rate(int model_index) const
Returns the number of frames of animation for this particular component in the indicated model.
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:35
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
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...
double get_frame_rate(int model_index) const
Returns the stated frame rate of the specified model.
void rename_char(const std::string &name)
Renames all of the models in the character data to the indicated name.
size_t estimate_db_size() const
Returns the estimated amount of memory, in megabytes, that will be required to perform the do_reparen...
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.