further progress on egg-optchar

This commit is contained in:
David Rose 2003-07-20 15:49:22 +00:00
parent ffbc370236
commit 986f3f8bdc
24 changed files with 945 additions and 41 deletions

View File

@ -7,12 +7,12 @@
dtoolutil:c dtoolbase:c dconfig:c dtoolconfig:m dtool:m pystub
#define UNIX_SYS_LIBS m
#begin test_bin_target
#define TARGET egg-optchar
#begin bin_target
#define TARGET egg-optchar-new
#define SOURCES \
config_egg_optchar.cxx config_egg_optchar.h \
eggOptchar.cxx eggOptchar.h \
eggOptcharUserData.I eggOptcharUserData.cxx eggOptcharUserData.h
#end test_bin_target
#end bin_target

View File

@ -17,16 +17,16 @@
////////////////////////////////////////////////////////////////////
#include "eggOptchar.h"
#include "eggOptcharUserData.h"
#include "dcast.h"
#include "eggJointData.h"
#include "eggSliderData.h"
#include "eggCharacterCollection.h"
#include "eggCharacterData.h"
#include "eggJointPointer.h"
#include "eggTable.h"
#include "compose_matrix.h"
#include "eggBackPointer.h"
#include "string_utils.h"
#include "pset.h"
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::Constructor
@ -49,6 +49,38 @@ EggOptchar() {
("ls", "", 0,
"List the joint hierarchy instead of performing any operations.",
&EggOptchar::dispatch_none, &_list_hierarchy);
add_option
("lp", "", 0,
"List the existing joint hierarchy as a series of -p joint,parent "
"commands, suitable for pasting into an egg-optchar command line.",
&EggOptchar::dispatch_none, &_list_hierarchy_p);
add_option
("keep", "joint", 0,
"Keep the named joint (or slider) in the character, even if it does "
"not appear to be needed by the animation.",
&EggOptchar::dispatch_vector_string, NULL, &_keep_components);
add_option
("expose", "joint", 0,
"Expose the named joint by flagging it with a DCS attribute, so "
"it can be found in the scene graph when the character is loaded, and "
"objects can be parented to it. This implies -keep.",
&EggOptchar::dispatch_vector_string, NULL, &_expose_components);
add_option
("keepall", "", 0,
"Keep all joints and sliders in the character.",
&EggOptchar::dispatch_none, &_keep_all);
add_option
("p", "joint,parent", 0,
"Moves the named joint under the named parent joint. Use "
"\"-p joint,\" to reparent a joint to the root. The joint transform "
"is recomputed appropriately under its new parent so that the animation "
"is not affected (the effect is similar to NodePath::wrt_reparent_to).",
&EggOptchar::dispatch_vector_string_pair, NULL, &_reparent_joints);
}
////////////////////////////////////////////////////////////////////
@ -58,13 +90,22 @@ EggOptchar() {
////////////////////////////////////////////////////////////////////
void EggOptchar::
run() {
int num_characters = _collection->get_num_characters();
// We have to apply the user-specified reparent requests first,
// before we even analyze the joints. This is because reparenting
// the joints may change their properties.
if (apply_user_reparents()) {
cerr << "Reparenting hierarchy.\n";
// So we'll have to call do_reparent() twice. It seems wasteful,
// but it really is necessary, and it's not that bad.
do_reparent();
}
int num_characters = _collection->get_num_characters();
int ci;
// Now we can analyze the joints for their properties.
for (ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
nout << "Processing " << char_data->get_name() << "\n";
analyze_joints(char_data->get_root_joint());
analyze_sliders(char_data);
}
@ -75,15 +116,27 @@ run() {
nout << "Character: " << char_data->get_name() << "\n";
list_joints(char_data->get_root_joint(), 0);
list_scalars(char_data);
nout << char_data->get_num_joints() << " joints.\n";
}
} else if (_list_hierarchy_p) {
for (ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
nout << "Character: " << char_data->get_name() << "\n";
list_joints_p(char_data->get_root_joint());
// A newline to cout is needed after the above call.
cout << "\n";
nout << char_data->get_num_joints() << " joints.\n";
}
} else {
// Now, trigger the actual rebuilding of all the joint data.
for (ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
char_data->get_root_joint()->do_rebuild();
// The meat of the program: determine which joints are to be
// removed, and then actually remove them.
determine_removed_components();
if (remove_joints()) {
do_reparent();
}
write_eggs();
}
}
@ -105,6 +158,236 @@ handle_args(ProgramBase::Args &args) {
return EggCharacterFilter::handle_args(args);
}
////////////////////////////////////////////////////////////////////
// Function: ProgramBase::dispatch_vector_string_pair
// Access: Protected, Static
// Description: Standard dispatch function for an option that takes
// a pair of string parameters. The data pointer is to
// StringPairs vector; the pair will be pushed onto the
// end of the vector.
////////////////////////////////////////////////////////////////////
bool EggOptchar::
dispatch_vector_string_pair(const string &opt, const string &arg, void *var) {
StringPairs *ip = (StringPairs *)var;
vector_string words;
tokenize(arg, words, ",");
if (words.size() == 2) {
StringPair sp;
sp._a = words[0];
sp._b = words[1];
ip->push_back(sp);
} else {
nout << "-" << opt
<< " requires a pair of strings separated by a comma.\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::determine_removed_components
// Access: Private
// Description: Flag all joints and sliders that should be removed
// for optimization purposes.
////////////////////////////////////////////////////////////////////
void EggOptchar::
determine_removed_components() {
typedef pset<string> Names;
Names keep_names;
Names expose_names;
Names names_used;
vector_string::const_iterator si;
for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
keep_names.insert(*si);
}
for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
keep_names.insert(*si);
expose_names.insert(*si);
}
// We always keep the root joint, which has no name.
keep_names.insert("");
int num_characters = _collection->get_num_characters();
for (int ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
int num_components = char_data->get_num_components();
for (int i = 0; i < num_components; i++) {
EggComponentData *comp_data = char_data->get_component(i);
EggOptcharUserData *user_data =
DCAST(EggOptcharUserData, comp_data->get_user_data());
const string &name = comp_data->get_name();
if (_keep_all || keep_names.find(name) != keep_names.end()) {
// Keep this component.
names_used.insert(name);
if (expose_names.find(name) != expose_names.end()) {
// In fact, expose it.
user_data->_flags |= EggOptcharUserData::F_expose;
}
} else {
// Remove this component if it's unanimated or empty.
if ((user_data->_flags & (EggOptcharUserData::F_static | EggOptcharUserData::F_empty)) != 0) {
user_data->_flags |= EggOptcharUserData::F_remove;
}
}
}
}
// Go back and tell the user about component names we didn't use,
// just to be helpful.
for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
const string &name = (*si);
if (names_used.find(name) == names_used.end()) {
nout << "No such joint: " << name << "\n";
}
}
for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
const string &name = (*si);
if (names_used.find(name) == names_used.end()) {
nout << "No such joint: " << name << "\n";
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::remove_joints
// Access: Private
// Description: Effects the actual removal of joints flagged for
// removal by reparenting the hierarchy appropriately.
// Returns true if anything is done, false otherwise.
////////////////////////////////////////////////////////////////////
bool EggOptchar::
remove_joints() {
bool did_anything = false;
int num_characters = _collection->get_num_characters();
for (int ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
int num_joints = char_data->get_num_joints();
int num_static = 0;
int num_empty = 0;
int num_identity = 0;
int num_kept = 0;
for (int i = 0; i < num_joints; i++) {
EggJointData *joint_data = char_data->get_joint(i);
EggOptcharUserData *user_data =
DCAST(EggOptcharUserData, joint_data->get_user_data());
EggJointData *best_parent = find_best_parent(joint_data->get_parent());
if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
// This joint will be removed, so reparent it to nothing.
joint_data->reparent_to((EggJointData *)NULL);
// Move the vertices associated with this joint into its
// parent.
joint_data->move_vertices_to(best_parent);
// Determine what kind of node it is we're removing, for the
// user's information.
if ((user_data->_flags & EggOptcharUserData::F_identity) != 0) {
num_identity++;
} else if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
num_static++;
} else if ((user_data->_flags & EggOptcharUserData::F_empty) != 0) {
num_empty++;
}
did_anything = true;
} else {
// This joint will be preserved, but maybe its parent will
// change.
joint_data->reparent_to(best_parent);
num_kept++;
}
}
if (num_joints == num_kept) {
nout << char_data->get_name() << ": keeping " << num_joints
<< " joints.\n";
} else {
nout << char_data->get_name() << ": of " << num_joints
<< " joints, removing " << num_identity << " identity, "
<< num_static << " static, and " << num_empty
<< " empty joints, leaving " << num_kept << ".\n";
}
}
return did_anything;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::find_best_parent
// Access: Private
// Description: Searches for this first joint at this level or above
// that is not scheduled to be removed. This is the
// joint that the first child of this joint should be
// reparented to.
////////////////////////////////////////////////////////////////////
EggJointData *EggOptchar::
find_best_parent(EggJointData *joint_data) const {
EggOptcharUserData *user_data =
DCAST(EggOptcharUserData, joint_data->get_user_data());
if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
// Keep going.
nassertr(joint_data->get_parent() != (EggJointData *)NULL, NULL);
return find_best_parent(joint_data->get_parent());
}
// This is the one!
return joint_data;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::apply_user_reparents
// Access: Private
// Description: Reparents all the joints that the user suggested on
// the command line. Returns true if any operations
// were performed, false otherwise.
////////////////////////////////////////////////////////////////////
bool EggOptchar::
apply_user_reparents() {
bool did_anything = false;
int num_characters = _collection->get_num_characters();
StringPairs::const_iterator spi;
for (spi = _reparent_joints.begin(); spi != _reparent_joints.end(); ++spi) {
const StringPair &p = (*spi);
for (int ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
EggJointData *node_a = char_data->find_joint(p._a);
EggJointData *node_b = char_data->get_root_joint();
if (!p._b.empty()) {
node_b = char_data->find_joint(p._b);
}
if (node_a == (EggJointData *)NULL) {
nout << "No joint named " << p._a << " in " << char_data->get_name()
<< ".\n";
} else if (node_b == (EggJointData *)NULL) {
nout << "No joint named " << p._b << " in " << char_data->get_name()
<< ".\n";
} else {
node_a->reparent_to(node_b);
did_anything = true;
}
}
}
return did_anything;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::analyze_joints
// Access: Private
@ -263,6 +546,29 @@ list_joints(EggJointData *joint_data, int indent_level) {
}
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::list_joints_p
// Access: Private
// Description: Outputs a list of the joint hierarchy as a series of
// -p joint,parent commands.
////////////////////////////////////////////////////////////////////
void EggOptchar::
list_joints_p(EggJointData *joint_data) {
// As above, don't list the root joint.
int num_children = joint_data->get_num_children();
for (int i = 0; i < num_children; i++) {
EggJointData *child_data = joint_data->get_child(i);
// We send output to cout instead of nout to avoid the
// word-wrapping, and also to allow the user to redirect this
// easily to a file.
cout << " -p " << child_data->get_name() << ","
<< joint_data->get_name();
list_joints_p(child_data);
}
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::list_scalars
// Access: Private
@ -284,20 +590,36 @@ list_scalars(EggCharacterData *char_data) {
////////////////////////////////////////////////////////////////////
void EggOptchar::
describe_component(EggComponentData *comp_data, int indent_level) {
indent(nout, indent_level)
// We use cout instead of nout so the user can easily redirect this
// to a file.
indent(cout, indent_level)
<< comp_data->get_name();
EggOptcharUserData *user_data =
DCAST(EggOptcharUserData, comp_data->get_user_data());
if (user_data->is_identity()) {
nout << " (identity)";
cout << " (identity)";
} else if (user_data->is_static()) {
nout << " (static)";
cout << " (static)";
}
if (user_data->is_empty()) {
nout << " (empty)";
cout << " (empty)";
}
cout << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::do_reparent
// Access: Private
// Description: Performs all of the queued up reparenting operations.
////////////////////////////////////////////////////////////////////
void EggOptchar::
do_reparent() {
int num_characters = _collection->get_num_characters();
for (int ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
char_data->do_reparent();
}
nout << "\n";
}

View File

@ -25,6 +25,7 @@
#include "luse.h"
#include "pvector.h"
#include "vector_string.h"
class EggCharacterData;
class EggComponentData;
@ -49,13 +50,35 @@ protected:
virtual bool handle_args(Args &args);
private:
static bool dispatch_vector_string_pair(const string &opt, const string &arg, void *var);
void determine_removed_components();
bool remove_joints();
EggJointData *find_best_parent(EggJointData *joint_data) const;
bool apply_user_reparents();
void analyze_joints(EggJointData *joint_data);
void analyze_sliders(EggCharacterData *char_data);
void list_joints(EggJointData *joint_data, int indent_level);
void list_joints_p(EggJointData *joint_data);
void list_scalars(EggCharacterData *char_data);
void describe_component(EggComponentData *comp_data, int indent_level);
void do_reparent();
bool _list_hierarchy;
bool _list_hierarchy_p;
bool _keep_all;
class StringPair {
public:
string _a;
string _b;
};
typedef pvector<StringPair> StringPairs;
StringPairs _reparent_joints;
vector_string _keep_components;
vector_string _expose_components;
};
#endif

View File

@ -43,7 +43,8 @@ public:
F_static = 0x0001,
F_identity = 0x0002,
F_empty = 0x0004,
F_remove = 0x0008
F_remove = 0x0008,
F_expose = 0x0010,
};
int _flags;
LMatrix4d _static_mat;

View File

@ -12,7 +12,8 @@
eggCharacterCollection.h eggCharacterCollection.I \
eggCharacterData.h eggCharacterData.I eggCharacterFilter.h \
eggComponentData.h eggComponentData.I eggJointData.h \
eggJointData.I eggJointPointer.h eggJointNodePointer.h \
eggJointData.I eggJointPointer.h eggJointPointer.I \
eggJointNodePointer.h \
eggMatrixTablePointer.h eggScalarTablePointer.h \
eggSliderData.h eggSliderData.I \
eggVertexPointer.h
@ -32,7 +33,7 @@
eggCharacterData.I eggCharacterData.h eggCharacterFilter.h \
eggComponentData.I eggComponentData.h \
eggJointData.h eggJointData.I \
eggJointPointer.h \
eggJointPointer.h eggJointPointer.I \
eggJointNodePointer.h \
eggMatrixTablePointer.h \
eggScalarTablePointer.h \

View File

@ -112,3 +112,13 @@ get_character_by_model_index(int model_index) const {
(EggCharacterData *)NULL);
return _characters_by_model_index[model_index];
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterCollection::ModelDescription::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE EggCharacterCollection::ModelDescription::
ModelDescription() {
_root_node = (EggObject *)NULL;
}

View File

@ -98,7 +98,7 @@ add_egg(EggData *egg) {
TopEggNodes::iterator ti;
for (ti = top_nodes.begin(); ti != top_nodes.end(); ++ti) {
EggNode *model_root = (*ti).first;
EggNodeList &egg_nodes = (*ti).second;
ModelDescription &desc = (*ti).second;
int model_index = _next_model_index++;
if (egg_info._models.empty()) {
@ -109,8 +109,9 @@ add_egg(EggData *egg) {
char_data->add_model(model_index, model_root);
nassertr(model_index == (int)_characters_by_model_index.size(), -1);
_characters_by_model_index.push_back(char_data);
root_joint->add_back_pointer(model_index, desc._root_node);
match_egg_nodes(char_data, root_joint, egg_nodes,
match_egg_nodes(char_data, root_joint, desc._top_nodes,
egg_index, model_index);
scan_for_morphs(model_root, model_index, char_data);
@ -268,12 +269,16 @@ scan_for_top_joints(EggNode *egg_node, EggNode *model_root,
EggGroup *group = DCAST(EggGroup, egg_node);
if (group->has_lod()) {
// This flag has an LOD specification.
// This group has an LOD specification; that indicates multiple
// skeleton hierarchies for this character, one for each LOD.
// We call each of these a separate model.
model_root = group;
}
if (group->get_group_type() == EggGroup::GT_joint) {
// A <Joint> node begins a model hierarchy.
_top_egg_nodes[character_name][model_root].push_back(group);
ModelDescription &desc = _top_egg_nodes[character_name][model_root];
desc._root_node = model_root;
desc._top_nodes.push_back(group);
return;
}
}
@ -307,12 +312,14 @@ scan_for_top_tables(EggTable *bundle, EggNode *model_root,
if (table->get_name() == "<skeleton>") {
// Here it is! Now the immediate children of this node are
// the top tables.
ModelDescription &desc = _top_egg_nodes[character_name][model_root];
desc._root_node = table;
EggGroupNode::iterator cgi;
for (cgi = table->begin(); cgi != table->end(); ++cgi) {
EggNode *grandchild = (*cgi);
if (grandchild->is_of_type(EggTable::get_class_type())) {
_top_egg_nodes[character_name][model_root].push_back(grandchild);
desc._top_nodes.push_back(grandchild);
}
}
}
@ -459,8 +466,10 @@ match_egg_nodes(EggCharacterData *char_data, EggJointData *joint_data,
EggNode *egg_node = (*ei);
EggJointData *data = make_joint_data(char_data);
joint_data->_children.push_back(data);
char_data->_joints.push_back(data);
char_data->_components.push_back(data);
data->_parent = joint_data;
data->_new_parent = joint_data;
found_egg_match(char_data, data, egg_node, egg_index, model_index);
}
@ -565,8 +574,10 @@ match_egg_nodes(EggCharacterData *char_data, EggJointData *joint_data,
EggNode *egg_node = (*ei);
EggJointData *data = make_joint_data(char_data);
joint_data->_children.push_back(data);
char_data->_joints.push_back(data);
char_data->_components.push_back(data);
data->_parent = joint_data;
data->_new_parent = joint_data;
found_egg_match(char_data, data, egg_node, egg_index, model_index);
}
}

View File

@ -19,13 +19,13 @@
#ifndef EGGCHARACTERCOLLECTION_H
#define EGGCHARACTERCOLLECTION_H
#include <pandatoolbase.h>
#include "pandatoolbase.h"
#include "eggCharacterData.h"
#include <eggData.h>
#include <eggNode.h>
#include <pointerTo.h>
#include "eggData.h"
#include "eggNode.h"
#include "pointerTo.h"
class EggTable;
class EggAttributes;
@ -66,7 +66,7 @@ public:
class EggInfo {
public:
PT(EggData) _egg;
typedef pvector<PT(EggNode)> Models;
typedef pvector< PT(EggNode) > Models;
Models _models;
int _first_model_index;
};
@ -95,7 +95,14 @@ private:
// The _top_egg_nodes member is only used temporarily, when adding
// each pre-existing egg file to the structure for the first time.
typedef pvector<EggNode *> EggNodeList;
typedef pmap<EggNode *, EggNodeList> TopEggNodes;
class ModelDescription {
public:
INLINE ModelDescription();
EggNodeList _top_nodes;
EggObject *_root_node;
};
typedef pmap<EggNode *, ModelDescription> TopEggNodes;
typedef pmap<string, TopEggNodes> TopEggNodesByName;
TopEggNodesByName _top_egg_nodes;

View File

@ -94,6 +94,30 @@ find_joint(const string &name) const {
return _root_joint->find_joint(name);
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::get_num_joints
// Access: Public
// Description: Returns the total number of joints in the character
// joint hierarchy.
////////////////////////////////////////////////////////////////////
INLINE int EggCharacterData::
get_num_joints() const {
return _joints.size();
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::get_joint
// Access: Public
// Description: Returns the nth joint in the character joint
// hierarchy. This returns all of the joints in the
// hierarchy in an arbitrary ordering.
////////////////////////////////////////////////////////////////////
INLINE EggJointData *EggCharacterData::
get_joint(int n) const {
nassertr(n >= 0 && n < (int)_joints.size(), NULL);
return _joints[n];
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::get_num_sliders
// Access: Public

View File

@ -72,6 +72,117 @@ add_model(int model_index, EggNode *model_root) {
_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::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);
}
}
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 " << joint_data->get_parent()->get_name()
<< " results in a skew transform.\n";
}
}
return invalid_set.empty();
}
////////////////////////////////////////////////////////////////////
// Function: EggCharacterData::find_slider
// Access: Public

View File

@ -68,9 +68,13 @@ public:
INLINE int get_num_models() const;
INLINE int get_model_index(int n) const;
INLINE EggNode *get_model_root(int n) const;
int get_num_frames(int model_index) const;
INLINE EggJointData *get_root_joint() const;
INLINE EggJointData *find_joint(const string &name) const;
INLINE int get_num_joints() const;
INLINE EggJointData *get_joint(int n) const;
bool do_reparent();
INLINE int get_num_sliders() const;
INLINE EggSliderData *get_slider(int n) const;
@ -100,6 +104,9 @@ protected:
typedef pvector<EggSliderData *> Sliders;
Sliders _sliders;
typedef pvector<EggJointData *> Joints;
Joints _joints;
typedef pvector<EggComponentData *> Components;
Components _components;

View File

@ -45,6 +45,8 @@ public:
void add_name(const string &name);
bool matches_name(const string &name) const;
virtual int get_num_frames(int model_index) const=0;
virtual void add_back_pointer(int model_index, EggObject *egg_object)=0;
virtual void write(ostream &out, int indent_level = 0) const=0;

View File

@ -47,3 +47,17 @@ get_child(int n) const {
nassertr(n >= 0 && n < (int)_children.size(), (EggJointData *)NULL);
return _children[n];
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::reparent_to
// Access: Public
// Description: Indicates an intention to change the parent of this
// joint to the indicated joint, or NULL to remove it
// from the hierarchy. The joint is not reparented
// immediately, but rather all of the joints are
// reparented at once when do_reparent() is called.
////////////////////////////////////////////////////////////////////
INLINE void EggJointData::
reparent_to(EggJointData *new_parent) {
_new_parent = new_parent;
}

View File

@ -39,6 +39,7 @@ EggJointData(EggCharacterCollection *collection,
EggComponentData(collection, char_data)
{
_parent = (EggJointData *)NULL;
_new_parent = (EggJointData *)NULL;
}
////////////////////////////////////////////////////////////////////
@ -66,7 +67,7 @@ find_joint(const string &name) {
////////////////////////////////////////////////////////////////////
// Function: EggJointData::get_num_frames
// Access: Public
// Access: Public, Virtual
// Description: Returns the number of frames of animation for this
// particular joint in the indicated model.
////////////////////////////////////////////////////////////////////
@ -119,6 +120,26 @@ get_net_frame(int model_index, int n) const {
return mat;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::move_vertices_to
// Access: Public
// Description: Moves the vertices assigned to this joint into the
// indicated joint, without changing their weight
// assignments.
////////////////////////////////////////////////////////////////////
void EggJointData::
move_vertices_to(EggJointData *new_owner) {
int num_models = get_num_models();
for (int model_index = 0; model_index < num_models; model_index++) {
if (has_model(model_index) && new_owner->has_model(model_index)) {
EggJointPointer *joint, *new_joint;
DCAST_INTO_V(joint, get_model(model_index));
DCAST_INTO_V(new_joint, new_owner->get_model(model_index));
joint->move_vertices_to(new_joint);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::do_rebuild
// Access: Public
@ -186,6 +207,7 @@ optimize() {
////////////////////////////////////////////////////////////////////
void EggJointData::
add_back_pointer(int model_index, EggObject *egg_object) {
nassertv(egg_object != (EggObject *)NULL);
if (egg_object->is_of_type(EggGroup::get_class_type())) {
// It must be a <Joint>.
EggJointNodePointer *joint = new EggJointNodePointer(egg_object);
@ -226,3 +248,196 @@ write(ostream &out, int indent_level) const {
indent(out, indent_level) << "}\n";
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::do_begin_reparent
// Access: Protected
// Description: Clears out the _children vector in preparation for
// refilling it from the _new_parent information.
////////////////////////////////////////////////////////////////////
void EggJointData::
do_begin_reparent() {
_children.clear();
int num_models = get_num_models();
for (int model_index = 0; model_index < num_models; model_index++) {
if (has_model(model_index)) {
EggJointPointer *joint;
DCAST_INTO_V(joint, get_model(model_index));
joint->begin_rebuild();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::do_begin_compute_reparent
// Access: Protected
// Description: Eliminates any cached values before beginning a walk
// through all the joints for do_compute_reparent(), for
// a given model/frame.
////////////////////////////////////////////////////////////////////
void EggJointData::
do_begin_compute_reparent() {
_got_new_net_frame = false;
_got_new_net_frame_inv = false;
_computed_reparent = false;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::do_compute_reparent
// Access: Protected
// Description: Prepares the reparent operation by computing a new
// transform for each frame of each model, designed to
// keep the net transform the same when the joint is
// moved to its new parent. Returns true on success,
// false on failure.
////////////////////////////////////////////////////////////////////
bool EggJointData::
do_compute_reparent(int model_index, int n) {
if (_computed_reparent) {
// We've already done this joint. This is possible because we
// have to recursively compute joints upwards, so we might visit
// the same joint more than once.
return _computed_ok;
}
_computed_reparent = true;
if (_parent == _new_parent) {
// Trivial (and most common) case: we are not moving the joint.
// No recomputation necessary.
_computed_ok = true;
return true;
}
EggBackPointer *back = get_model(model_index);
if (back == (EggBackPointer *)NULL) {
// This joint doesn't have any data to modify.
_computed_ok = true;
return true;
}
EggJointPointer *joint;
DCAST_INTO_R(joint, back, false);
LMatrix4d transform;
if (_parent == (EggJointData *)NULL) {
// We are moving from outside the joint hierarchy to within it.
transform = _new_parent->get_new_net_frame_inv(model_index, n);
} else if (_new_parent == (EggJointData *)NULL) {
// We are moving from within the hierarchy to outside it.
transform = _parent->get_new_net_frame(model_index, n);
} else {
// We are changing parents within the hierarchy.
transform =
_parent->get_new_net_frame(model_index, n) *
_new_parent->get_new_net_frame_inv(model_index, n);
}
nassertr(n == joint->get_num_rebuild_frames(), false);
_computed_ok = joint->add_rebuild_frame(joint->get_frame(n) * transform);
return _computed_ok;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::do_finish_reparent
// Access: Protected
// Description: Performs the actual reparenting operation
// by removing all of the old children and replacing
// them with the set of new children. Returns true on
// success, false on failure.
////////////////////////////////////////////////////////////////////
bool EggJointData::
do_finish_reparent() {
bool all_ok = true;
int num_models = get_num_models();
for (int model_index = 0; model_index < num_models; model_index++) {
EggJointPointer *parent_joint = NULL;
if (_new_parent != NULL && _new_parent->has_model(model_index)) {
DCAST_INTO_R(parent_joint, _new_parent->get_model(model_index), false);
}
if (has_model(model_index)) {
EggJointPointer *joint;
DCAST_INTO_R(joint, get_model(model_index), false);
joint->do_finish_reparent(parent_joint);
if (!joint->do_rebuild()) {
all_ok = false;
}
}
}
_parent = _new_parent;
if (_parent != (EggJointData *)NULL) {
_parent->_children.push_back(this);
}
return all_ok;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::get_new_net_frame
// Access: Private
// Description: Similar to get_net_frame(), but computed for the
// prospective new parentage of the node, before
// do_finish_reparent() is called. This is generally
// useful only when called within do_compute_reparent().
////////////////////////////////////////////////////////////////////
const LMatrix4d &EggJointData::
get_new_net_frame(int model_index, int n) {
if (!_got_new_net_frame) {
_new_net_frame = get_new_frame(model_index, n);
if (_new_parent != (EggJointData *)NULL) {
_new_net_frame = _new_net_frame * _new_parent->get_new_net_frame(model_index, n);
}
_got_new_net_frame = true;
}
return _new_net_frame;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::get_new_net_frame_inv
// Access: Private
// Description: Returns the inverse of get_new_net_frame().
////////////////////////////////////////////////////////////////////
const LMatrix4d &EggJointData::
get_new_net_frame_inv(int model_index, int n) {
if (!_got_new_net_frame_inv) {
_new_net_frame_inv.invert_from(get_new_frame(model_index, n));
if (_new_parent != (EggJointData *)NULL) {
_new_net_frame_inv = _new_parent->get_new_net_frame_inv(model_index, n) * _new_net_frame_inv;
}
_got_new_net_frame_inv = true;
}
return _new_net_frame_inv;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::get_new_frame
// Access: Private
// Description: Returns the local transform matrix corresponding to
// this joint position in the nth frame in the indicated
// model, as it will be when do_finish_reparent() is
// called.
////////////////////////////////////////////////////////////////////
LMatrix4d EggJointData::
get_new_frame(int model_index, int n) {
do_compute_reparent(model_index, n);
EggBackPointer *back = get_model(model_index);
if (back == (EggBackPointer *)NULL) {
return LMatrix4d::ident_mat();
}
EggJointPointer *joint;
DCAST_INTO_R(joint, back, LMatrix4d::ident_mat());
if (joint->get_num_rebuild_frames() > 0) {
return joint->get_rebuild_frame(n);
} else {
return joint->get_frame(n);
}
}

View File

@ -43,22 +43,42 @@ public:
INLINE EggJointData *get_child(int n) const;
EggJointData *find_joint(const string &name);
int get_num_frames(int model_index) const;
virtual int get_num_frames(int model_index) const;
LMatrix4d get_frame(int model_index, int n) const;
LMatrix4d get_net_frame(int model_index, int n) const;
INLINE void reparent_to(EggJointData *new_parent);
void move_vertices_to(EggJointData *new_owner);
bool do_rebuild();
void optimize();
virtual void add_back_pointer(int model_index, EggObject *egg_object);
virtual void write(ostream &out, int indent_level = 0) const;
protected:
void do_begin_reparent();
void do_begin_compute_reparent();
bool do_compute_reparent(int model_index, int n);
bool do_finish_reparent();
private:
const LMatrix4d &get_new_net_frame(int model_index, int n);
const LMatrix4d &get_new_net_frame_inv(int model_index, int n);
LMatrix4d get_new_frame(int model_index, int n);
// These are used to cache the above results for optimizing
// do_compute_reparent().
LMatrix4d _new_net_frame, _new_net_frame_inv;
bool _got_new_net_frame, _got_new_net_frame_inv;
bool _computed_reparent;
bool _computed_ok;
protected:
typedef pvector<EggJointData *> Children;
Children _children;
EggJointData *_parent;
friend class EggCharacterCollection;
EggJointData *_new_parent;
public:
@ -77,6 +97,9 @@ public:
private:
static TypeHandle _type_handle;
friend class EggCharacterCollection;
friend class EggCharacterData;
};
#include "eggJointData.I"

View File

@ -35,7 +35,7 @@ EggJointNodePointer::
EggJointNodePointer(EggObject *object) {
_joint = DCAST(EggGroup, object);
if (_joint != (EggGroup *)NULL) {
if (_joint != (EggGroup *)NULL && _joint->is_joint()) {
// Quietly insist that the joint has a transform, for neatness. If
// it does not, give it the identity transform.
if (!_joint->has_transform()) {
@ -94,6 +94,46 @@ set_frame(int n, const LMatrix4d &mat) {
_joint->set_transform(mat);
}
////////////////////////////////////////////////////////////////////
// Function: EggJointNodePointer::do_finish_reparent
// Access: Protected
// Description: Performs the actual reparenting operation
// by removing the node from its old parent and
// associating it with its new parent, if any.
////////////////////////////////////////////////////////////////////
void EggJointNodePointer::
do_finish_reparent(EggJointPointer *new_parent) {
if (new_parent == (EggJointPointer *)NULL) {
// No new parent; unparent the joint.
EggGroupNode *egg_parent = _joint->get_parent();
if (egg_parent != (EggGroupNode *)NULL) {
egg_parent->remove_child(_joint.p());
}
} else {
// Reparent the joint to its new parent (implicitly unparenting it
// from its previous parent).
EggJointNodePointer *new_node = DCAST(EggJointNodePointer, new_parent);
if (new_node->_joint != _joint->get_parent()) {
new_node->_joint->add_child(_joint.p());
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggJointNodePointer::move_vertices_to
// Access: Public, Virtual
// Description: Moves the vertices assigned to this joint into the
// other joint (which should be of the same type).
////////////////////////////////////////////////////////////////////
void EggJointNodePointer::
move_vertices_to(EggJointPointer *new_joint) {
EggJointNodePointer *new_node;
DCAST_INTO_V(new_node, new_joint);
new_node->_joint->steal_vrefs(_joint);
}
////////////////////////////////////////////////////////////////////
// Function: EggJointNodePointer::add_rebuild_frame
// Access: Public, Virtual

View File

@ -38,6 +38,9 @@ public:
virtual LMatrix4d get_frame(int n) const;
virtual void set_frame(int n, const LMatrix4d &mat);
virtual void do_finish_reparent(EggJointPointer *new_parent);
virtual void move_vertices_to(EggJointPointer *new_joint);
virtual bool add_rebuild_frame(const LMatrix4d &mat);
virtual bool do_rebuild();

View File

@ -0,0 +1,41 @@
// Filename: eggJointPointer.I
// Created by: drose (20Jul03)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: EggJointPointer::get_num_rebuild_frames
// Access: Public
// Description: Returns the number of rebuild frames that have been
// added so far.
////////////////////////////////////////////////////////////////////
INLINE int EggJointPointer::
get_num_rebuild_frames() const {
return _rebuild_frames.size();
}
////////////////////////////////////////////////////////////////////
// Function: EggJointPointer::get_rebuild_frame
// Access: Public
// Description: Returns the nth matrix that has been added to the set
// of rebuild frames.
////////////////////////////////////////////////////////////////////
INLINE const LMatrix4d &EggJointPointer::
get_rebuild_frame(int n) const {
nassertr(n >= 0 && n < (int)_rebuild_frames.size(), LMatrix4d::ident_mat());
return _rebuild_frames[n];
}

View File

@ -34,6 +34,16 @@ add_frame(const LMatrix4d &) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: EggJointPointer::move_vertices_to
// Access: Public, Virtual
// Description: Moves the vertices assigned to this joint into the
// other joint (which should be of the same type).
////////////////////////////////////////////////////////////////////
void EggJointPointer::
move_vertices_to(EggJointPointer *) {
}
////////////////////////////////////////////////////////////////////
// Function: EggJointPointer::begin_rebuild
// Access: Public

View File

@ -41,8 +41,13 @@ public:
virtual void set_frame(int n, const LMatrix4d &mat)=0;
virtual bool add_frame(const LMatrix4d &mat);
virtual void do_finish_reparent(EggJointPointer *new_parent)=0;
virtual void move_vertices_to(EggJointPointer *new_joint);
void begin_rebuild();
virtual bool add_rebuild_frame(const LMatrix4d &mat);
INLINE int get_num_rebuild_frames() const;
INLINE const LMatrix4d &get_rebuild_frame(int n) const;
virtual bool do_rebuild();
virtual void optimize();
@ -69,6 +74,8 @@ private:
static TypeHandle _type_handle;
};
#include "eggJointPointer.I"
#endif

View File

@ -86,6 +86,10 @@ get_frame(int n) const {
// If we have exactly one frame, then we have as many frames as we
// want; just repeat the first frame.
n = 0;
} else if (get_num_frames() == 0) {
// If we have no frames, we really have the identity matrix.
return LMatrix4d::ident_mat();
}
nassertr(n >= 0 && n < get_num_frames(), LMatrix4d::ident_mat());
@ -122,6 +126,32 @@ add_frame(const LMatrix4d &mat) {
return _xform->add_data(mat);
}
////////////////////////////////////////////////////////////////////
// Function: EggMatrixTablePointer::do_finish_reparent
// Access: Protected
// Description: Performs the actual reparenting operation
// by removing the node from its old parent and
// associating it with its new parent, if any.
////////////////////////////////////////////////////////////////////
void EggMatrixTablePointer::
do_finish_reparent(EggJointPointer *new_parent) {
if (new_parent == (EggJointPointer *)NULL) {
// No new parent; unparent the joint.
EggGroupNode *egg_parent = _table->get_parent();
if (egg_parent != (EggGroupNode *)NULL) {
egg_parent->remove_child(_table.p());
}
} else {
// Reparent the joint to its new parent (implicitly unparenting it
// from its previous parent).
EggMatrixTablePointer *new_node = DCAST(EggMatrixTablePointer, new_parent);
if (new_node->_table != _table->get_parent()) {
new_node->_table->add_child(_table.p());
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggMatrixTablePointer::do_rebuild
// Access: Public, Virtual

View File

@ -43,6 +43,8 @@ public:
virtual void set_frame(int n, const LMatrix4d &mat);
virtual bool add_frame(const LMatrix4d &mat);
virtual void do_finish_reparent(EggJointPointer *new_parent);
virtual bool do_rebuild();
virtual void optimize();

View File

@ -42,7 +42,7 @@ EggSliderData(EggCharacterCollection *collection,
////////////////////////////////////////////////////////////////////
// Function: EggSliderData::get_num_frames
// Access: Public
// Access: Public, Virtual
// Description: Returns the number of frames of animation for this
// particular slider in the indicated model.
////////////////////////////////////////////////////////////////////

View File

@ -37,7 +37,7 @@ public:
EggSliderData(EggCharacterCollection *collection,
EggCharacterData *char_data);
int get_num_frames(int model_index) const;
virtual int get_num_frames(int model_index) const;
double get_frame(int model_index, int n) const;
virtual void add_back_pointer(int model_index, EggObject *egg_object);