Panda3D

Loading...
Searching...
No Matches
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 */
36EggCharacterData::
37EggCharacterData(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 */
48EggCharacterData::
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 */
65 void EggCharacterData::
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 */
84 void EggCharacterData::
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 */
99 int EggCharacterData::
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 */
143 bool EggCharacterData::
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 */
182 bool EggCharacterData::
183 do_reparent() {
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 */
293 void EggCharacterData::
294 choose_optimal_hierarchy() {
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 */
340 EggSliderData *EggCharacterData::
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 */
355 EggSliderData *EggCharacterData::
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::
378 estimate_db_size() const {
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::
400write(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}
EggCharacterCollection
Represents a set of characters, as read and collected from possibly several model and/or animation eg...
Definition eggCharacterCollection.h:32
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_root_joint
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
Definition eggCharacterData.I:71
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
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
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::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
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
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::make_slider
EggSliderData * make_slider(const std::string &name)
Returns the slider matching the indicated name.
Definition eggCharacterData.cxx:356
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::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
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
EggCharacterDb
This class is used during joint optimization or restructuring to store the table of interim joint com...
Definition eggCharacterDb.h:41
EggComponentData
This is the base class of both EggJointData and EggSliderData.
Definition eggComponentData.h:34
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
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
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
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
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
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
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition eggNode.h:36
EggSliderData
This corresponds to a single morph slider control.
Definition eggSliderData.h:28
eggCharacterCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggCharacterData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggCharacterDb.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggJointData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggSliderData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.