| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- /**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file eggCharacterData.cxx
- * @author drose
- * @date 2001-02-23
- */
- #include "eggCharacterData.h"
- #include "eggCharacterCollection.h"
- #include "eggCharacterDb.h"
- #include "eggJointData.h"
- #include "eggSliderData.h"
- #include "indent.h"
- #include <algorithm>
- // An STL function object to sort the joint list in order from highest to
- // lowest in the new hierarchy. Used in do_reparent().
- class OrderJointsByNewDepth {
- public:
- bool operator()(const EggJointData *a, const EggJointData *b) const {
- return a->_new_parent_depth < b->_new_parent_depth;
- }
- };
- /**
- *
- */
- EggCharacterData::
- EggCharacterData(EggCharacterCollection *collection) :
- _component_names("_", "joint_")
- {
- _collection = collection;
- _root_joint = _collection->make_joint_data(this);
- // The fictitious root joint is not added to the _components list.
- }
- /**
- *
- */
- EggCharacterData::
- ~EggCharacterData() {
- delete _root_joint;
- Sliders::iterator si;
- for (si = _sliders.begin(); si != _sliders.end(); ++si) {
- EggSliderData *slider = (*si);
- delete slider;
- }
- }
- /**
- * Renames all of the models in the character data to the indicated name.
- * This is the name that is used to identify unique skeleton hierarchies; if
- * you set two different models to the same name, they will be loaded together
- * as if they are expected to have the same skeleton hierarchy.
- */
- void EggCharacterData::
- rename_char(const std::string &name) {
- Models::iterator mi;
- for (mi = _models.begin(); mi != _models.end(); ++mi) {
- (*mi)._model_root->set_name(name);
- }
- set_name(name);
- }
- /**
- * Indicates that the given model_index (with the indicated model_root) is
- * associated with this character. This is normally called by the
- * EggCharacterCollection class as new models are discovered.
- *
- * A "model" here is either a character model (or one LOD of a character
- * model), or a character animation file: in either case, a hierarchy of
- * joints.
- */
- void EggCharacterData::
- add_model(int model_index, EggNode *model_root, EggData *egg_data) {
- Model m;
- m._model_index = model_index;
- m._model_root = model_root;
- m._egg_data = egg_data;
- _models.push_back(m);
- }
- /**
- * Returns the number of frames of animation of the indicated model. This is
- * more reliable than asking a particular joint or slider of the animation for
- * its number of frames, since a particular joint may have only 1 frame (if it
- * is unanimated), even though the overall animation has many frames.
- */
- int EggCharacterData::
- get_num_frames(int model_index) const {
- int max_num_frames = 0;
- Components::const_iterator ci;
- for (ci = _components.begin(); ci != _components.end(); ++ci) {
- EggComponentData *component = (*ci);
- int num_frames = component->get_num_frames(model_index);
- if (num_frames > 1) {
- // We have a winner. Assume all other components will be similar.
- return num_frames;
- }
- max_num_frames = std::max(max_num_frames, num_frames);
- }
- // Every component had either 1 frame or 0 frames. Return the maximum of
- // these.
- return max_num_frames;
- }
- /**
- * Returns the stated frame rate of the specified model. Similar to
- * get_num_frames().
- */
- double EggCharacterData::
- get_frame_rate(int model_index) const {
- Components::const_iterator ci;
- for (ci = _components.begin(); ci != _components.end(); ++ci) {
- EggComponentData *component = (*ci);
- double frame_rate = component->get_frame_rate(model_index);
- if (frame_rate != 0.0) {
- // We have a winner. Assume all other components will be similar.
- return frame_rate;
- }
- }
- return 0.0;
- }
- /**
- * Walks through each component and ensures that all have the same number of
- * frames of animation (except for those that contain 0 or 1 frames, of
- * course). Returns true if all are valid, false if there is a discreprency
- * (in which case the shorter component are extended).
- */
- bool EggCharacterData::
- check_num_frames(int model_index) {
- int max_num_frames = 0;
- bool any_violations = false;
- Components::const_iterator ci;
- for (ci = _components.begin(); ci != _components.end(); ++ci) {
- EggComponentData *component = (*ci);
- int num_frames = component->get_num_frames(model_index);
- if (num_frames > 1 && max_num_frames > 1 &&
- max_num_frames != num_frames) {
- // If we have two different opinions about the number of frames (other
- // than 0 or 1), we have a discrepency. This is an error condition.
- any_violations = true;
- }
- max_num_frames = std::max(max_num_frames, num_frames);
- }
- if (any_violations) {
- // Now go back through and force all components to the appropriate length.
- for (ci = _components.begin(); ci != _components.end(); ++ci) {
- EggComponentData *component = (*ci);
- int num_frames = component->get_num_frames(model_index);
- if (num_frames > 1 && max_num_frames != num_frames) {
- component->extend_to(model_index, max_num_frames);
- }
- }
- }
- return !any_violations;
- }
- /**
- * Begins the process of restructuring the joint hierarchy according to the
- * previous calls to reparent_to() on various joints. This will reparent the
- * joint hierachy in all models as requested, while adjusting the transforms
- * as appropriate so that each joint retains the same net transform across all
- * frames that it had before the operation. Returns true on success, false on
- * failure.
- */
- bool EggCharacterData::
- do_reparent() {
- typedef pset<EggJointData *> InvalidSet;
- InvalidSet invalid_set;
- // To begin, make sure the list of new_children is accurate.
- Joints::const_iterator ji;
- for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
- EggJointData *joint_data = (*ji);
- joint_data->do_begin_reparent();
- }
- // We also need to clear the children on the root joint, but the root joint
- // doesn't get any of the other operations (including finish_reparent)
- // applied to it.
- _root_joint->do_begin_reparent();
- // Now, check for cycles in the new parenting hierarchy, and also sort the
- // joints in order from top to bottom in the new hierarchy.
- for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
- EggJointData *joint_data = (*ji);
- pset<EggJointData *> chain;
- if (joint_data->calc_new_parent_depth(chain)) {
- nout << "Cycle detected in parent chain for " << joint_data->get_name()
- << "!\n";
- return false;
- }
- }
- sort(_joints.begin(), _joints.end(), OrderJointsByNewDepth());
- // Now compute the new transforms for the joints' new positions. This is
- // done recursively through the new parent hierarchy, so we can take
- // advantage of caching the net value for a particular frame.
- Models::const_iterator mi;
- for (mi = _models.begin(); mi != _models.end(); ++mi) {
- EggCharacterDb db;
- int model_index = (*mi)._model_index;
- int num_frames = get_num_frames(model_index);
- nout << " computing " << (mi - _models.begin()) + 1
- << " of " << _models.size()
- << ": " << (*mi)._egg_data->get_egg_filename()
- << " (" << num_frames << " frames)\n";
- for (int f = 0; f < num_frames; f++) {
- // First, walk through all the joints and flush the computed net
- // transforms from before.
- for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
- EggJointData *joint_data = (*ji);
- joint_data->do_begin_compute_reparent();
- }
- _root_joint->do_begin_compute_reparent();
- // Now go back through and compute the reparented transforms, caching
- // net transforms as necessary.
- for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
- EggJointData *joint_data = (*ji);
- if (!joint_data->do_compute_reparent(model_index, f, db)) {
- // Oops, we got an invalid transform.
- invalid_set.insert(joint_data);
- }
- }
- }
- // Finally, apply the computations to the joints.
- for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
- EggJointData *joint_data = (*ji);
- if (!joint_data->do_joint_rebuild(model_index, db)) {
- invalid_set.insert(joint_data);
- }
- }
- }
- // Now remove all of the old children and add in the new children.
- for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
- EggJointData *joint_data = (*ji);
- joint_data->do_finish_reparent();
- }
- // Report the set of joints that failed. It really shouldn't be possible
- // for any joints to fail, so if you see anything reported here, something
- // went wrong at a fundamental level. Perhaps a problem with
- // decompose_matrix().
- InvalidSet::const_iterator si;
- for (si = invalid_set.begin(); si != invalid_set.end(); ++si) {
- EggJointData *joint_data = (*si);
- // Don't bother reporting joints that no longer have a parent, since we
- // don't care about joints that are now outside the hierarchy.
- if (joint_data->get_parent() != nullptr) {
- nout << "Warning: reparenting " << joint_data->get_name()
- << " to ";
- if (joint_data->get_parent() == _root_joint) {
- nout << "the root";
- } else {
- nout << joint_data->get_parent()->get_name();
- }
- nout << " results in an invalid transform.\n";
- }
- }
- return invalid_set.empty();
- }
- /**
- * Chooses the best possible parent joint for each of the joints in the
- * hierarchy, based on the score computed by
- * EggJointData::score_reparent_to(). This is a fairly expensive operation
- * that involves lots of recomputing of transforms across the hierarchy.
- *
- * The joints are not actually reparented yet, but the new_parent of each
- * joint is set. Call do_reparent() to actually perform the suggested
- * reparenting operation.
- */
- void EggCharacterData::
- choose_optimal_hierarchy() {
- EggCharacterDb db;
- Joints::const_iterator ji, jj;
- for (ji = _joints.begin(); ji != _joints.end(); ++ji) {
- EggJointData *joint_data = (*ji);
- EggJointData *best_parent = joint_data->get_parent();
- int best_score = joint_data->score_reparent_to(best_parent, db);
- for (jj = _joints.begin(); jj != _joints.end(); ++jj) {
- EggJointData *possible_parent = (*jj);
- if (possible_parent != joint_data && possible_parent != best_parent &&
- !joint_data->is_new_ancestor(possible_parent)) {
- int score = joint_data->score_reparent_to(possible_parent, db);
- if (score >= 0 && (best_score < 0 || score < best_score)) {
- best_parent = possible_parent;
- best_score = score;
- }
- }
- }
- // Also consider reparenting the node to the root.
- EggJointData *possible_parent = get_root_joint();
- if (possible_parent != best_parent) {
- int score = joint_data->score_reparent_to(possible_parent, db);
- if (score >= 0 && (best_score < 0 || score < best_score)) {
- best_parent = possible_parent;
- best_score = score;
- }
- }
- if (best_parent != nullptr &&
- best_parent != joint_data->_parent) {
- nout << "best parent for " << joint_data->get_name() << " is "
- << best_parent->get_name() << "\n";
- joint_data->reparent_to(best_parent);
- }
- }
- }
- /**
- * Returns the slider with the indicated name, or NULL if no slider has that
- * name.
- */
- EggSliderData *EggCharacterData::
- find_slider(const std::string &name) const {
- SlidersByName::const_iterator si;
- si = _sliders_by_name.find(name);
- if (si != _sliders_by_name.end()) {
- return (*si).second;
- }
- return nullptr;
- }
- /**
- * Returns the slider matching the indicated name. If no such slider exists
- * already, creates a new one.
- */
- EggSliderData *EggCharacterData::
- make_slider(const std::string &name) {
- SlidersByName::const_iterator si;
- si = _sliders_by_name.find(name);
- if (si != _sliders_by_name.end()) {
- return (*si).second;
- }
- EggSliderData *slider = _collection->make_slider_data(this);
- slider->set_name(name);
- _sliders_by_name.insert(SlidersByName::value_type(name, slider));
- _sliders.push_back(slider);
- _components.push_back(slider);
- return slider;
- }
- /**
- * Returns the estimated amount of memory, in megabytes, that will be required
- * to perform the do_reparent() operation. This is used mainly be
- * EggCharacterDb to decide up front whether to store this data in-RAM or on-
- * disk.
- */
- size_t EggCharacterData::
- estimate_db_size() const {
- // Count how much memory we will need to store the interim transforms. This
- // is models * joints * frames * 3 * sizeof(LMatrix4d).
- size_t mj_frames = 0;
- Models::const_iterator mi;
- for (mi = _models.begin(); mi != _models.end(); ++mi) {
- int model_index = (*mi)._model_index;
- size_t num_frames = (size_t)get_num_frames(model_index);
- mj_frames += num_frames * _joints.size();
- }
- // We do this operation a bit carefully, to guard against integer overflow.
- size_t mb_needed = ((mj_frames * 3 / 1024) * sizeof(LMatrix4d)) / 1024;
- return mb_needed;
- }
- /**
- *
- */
- void EggCharacterData::
- write(std::ostream &out, int indent_level) const {
- indent(out, indent_level)
- << "Character " << get_name() << ":\n";
- get_root_joint()->write(out, indent_level + 2);
- Sliders::const_iterator si;
- for (si = _sliders.begin(); si != _sliders.end(); ++si) {
- EggSliderData *slider = (*si);
- slider->write(out, indent_level + 2);
- }
- }
|