open_toontown_panda3d/pandatool/src/eggcharbase/eggCharacterData.cxx

295 lines
11 KiB
C++

// Filename: eggCharacterData.cxx
// Created by: drose (23Feb01)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "eggCharacterData.h"
#include "eggCharacterCollection.h"
#include "eggJointData.h"
#include "eggSliderData.h"
#include "indent.h"
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggCharacterData::
EggCharacterData(EggCharacterCollection *collection) {
_collection = collection;
_root_joint = _collection->make_joint_data(this);
// The fictitious root joint is not added to the _components list.
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
EggCharacterData::
~EggCharacterData() {
delete _root_joint;
Sliders::iterator si;
for (si = _sliders.begin(); si != _sliders.end(); ++si) {
EggSliderData *slider = (*si);
delete slider;
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::add_model
// Access: Public
// Description: 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);
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::get_num_frames
// Access: Public
// Description: 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 = max(max_num_frames, num_frames);
}
// Every component had either 1 frame or 0 frames. Return the
// maximum of these.
return max_num_frames;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::check_num_frames
// Access: Public
// Description: 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 = 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;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::do_reparent
// Access: Public
// Description: 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;
// First, 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 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) {
int model_index = (*mi)._model_index;
int num_frames = get_num_frames(model_index);
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)) {
// Oops, we got an invalid transform.
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);
if (!joint_data->do_finish_reparent()) {
invalid_set.insert(joint_data);
}
}
// 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() != (EggJointData *)NULL) {
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();
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::find_slider
// Access: Public
// Description: Returns the slider with the indicated name, or NULL
// if no slider has that name.
////////////////////////////////////////////////////////////////////
EggSliderData *EggCharacterData::
find_slider(const string &name) const {
SlidersByName::const_iterator si;
si = _sliders_by_name.find(name);
if (si != _sliders_by_name.end()) {
return (*si).second;
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::make_slider
// Access: Public
// Description: Returns the slider matching the indicated name. If
// no such slider exists already, creates a new one.
////////////////////////////////////////////////////////////////////
EggSliderData *EggCharacterData::
make_slider(const 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;
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void EggCharacterData::
write(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);
}
}