add -zero, etc.

This commit is contained in:
David Rose 2003-07-22 01:50:40 +00:00
parent c37f62fd9a
commit 6b7baa43d7
9 changed files with 242 additions and 16 deletions

View File

@ -30,6 +30,7 @@
#include "string_utils.h"
#include "dcast.h"
#include "pset.h"
#include "compose_matrix.h"
#include <algorithm>
@ -76,6 +77,14 @@ EggOptchar() {
"and objects can be parented to it. This implies -keep.",
&EggOptchar::dispatch_vector_string_comma, NULL, &_expose_components);
add_option
("zero", "joint[,hprxyzijk]", 0,
"Zeroes out the animation channels for the named joint. If "
"a subset of the component letters hprxyzijk is included, the "
"operation is restricted to just those components; otherwise the "
"entire transform is cleared.",
&EggOptchar::dispatch_name_components, NULL, &_zero_channels);
add_option
("keepall", "", 0,
"Keep all joints and sliders in the character.",
@ -117,6 +126,10 @@ run() {
do_reparent();
}
if (!_zero_channels.empty()) {
zero_channels();
}
int num_characters = _collection->get_num_characters();
int ci;
@ -150,6 +163,7 @@ run() {
// The meat of the program: determine which joints are to be
// removed, and then actually remove them.
determine_removed_components();
move_vertices();
if (process_joints()) {
do_reparent();
}
@ -215,6 +229,55 @@ dispatch_vector_string_pair(const string &opt, const string &arg, void *var) {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: ProgramBase::dispatch_name_components
// Access: Protected, Static
// Description: Accepts a name optionally followed by a comma and some
// of the nine standard component letters,
//
// The data pointer is to StringPairs vector; the pair
// will be pushed onto the end of the vector.
////////////////////////////////////////////////////////////////////
bool EggOptchar::
dispatch_name_components(const string &opt, const string &arg, void *var) {
StringPairs *ip = (StringPairs *)var;
vector_string words;
tokenize(arg, words, ",");
StringPair sp;
if (words.size() == 1) {
sp._a = words[0];
} else if (words.size() == 2) {
sp._a = words[0];
sp._b = words[1];
} else {
nout << "-" << opt
<< " requires a pair of strings separated by a comma.\n";
return false;
}
if (sp._b.empty()) {
sp._b = matrix_components;
} else {
for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) {
if (strchr(matrix_components, *si) == NULL) {
nout << "Not a standard matrix component: \"" << *si << "\"\n"
<< "-" << opt << " requires a joint name followed by a set "
<< "of component names. The standard component names are \""
<< matrix_components << "\".\n";
return false;
}
}
}
ip->push_back(sp);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::determine_removed_components
// Access: Private
@ -284,6 +347,43 @@ determine_removed_components() {
}
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::move_vertices
// Access: Private
// Description: Moves the vertices from joints that are about to be
// removed into the first suitable parent. This might
// result in fewer joints being removed (because
// the parent might suddenly no longer be empty).
////////////////////////////////////////////////////////////////////
void EggOptchar::
move_vertices() {
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();
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());
if ((user_data->_flags & EggOptcharUserData::F_empty) == 0 &&
(user_data->_flags & EggOptcharUserData::F_remove) != 0) {
// This joint has vertices, but is scheduled to be removed;
// find a suitable home for its vertices.
EggJointData *best_joint = find_best_vertex_joint(joint_data->get_parent());
joint_data->move_vertices_to(best_joint);
// Now we can't remove the joint.
EggOptcharUserData *best_user_data =
DCAST(EggOptcharUserData, best_joint->get_user_data());
best_user_data->_flags &= ~(EggOptcharUserData::F_empty | EggOptcharUserData::F_remove);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::process_joints
// Access: Private
@ -310,16 +410,10 @@ process_joints() {
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) {
@ -334,6 +428,7 @@ process_joints() {
} else {
// This joint will be preserved, but maybe its parent will
// change.
EggJointData *best_parent = find_best_parent(joint_data->get_parent());
joint_data->reparent_to(best_parent);
if ((user_data->_flags & EggOptcharUserData::F_expose) != 0) {
joint_data->expose();
@ -348,7 +443,7 @@ process_joints() {
} else {
nout << char_data->get_name() << ": of " << num_joints
<< " joints, removing " << num_identity << " identity, "
<< num_static << " static, and " << num_empty
<< num_static << " unanimated, and " << num_empty
<< " empty joints, leaving " << num_kept << ".\n";
}
}
@ -359,7 +454,7 @@ process_joints() {
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::find_best_parent
// Access: Private
// Description: Searches for this first joint at this level or above
// Description: Searches for the 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.
@ -371,9 +466,33 @@ find_best_parent(EggJointData *joint_data) const {
if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
// Keep going.
nassertr(joint_data->get_parent() != (EggJointData *)NULL, NULL);
if (joint_data->get_parent() != (EggJointData *)NULL) {
return find_best_parent(joint_data->get_parent());
}
}
// This is the one!
return joint_data;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::find_best_vertex_joint
// Access: Private
// Description: Searches for the first joint at this level or above
// that is not static. This is the joint that the
// vertices of this joint should be moved into.
////////////////////////////////////////////////////////////////////
EggJointData *EggOptchar::
find_best_vertex_joint(EggJointData *joint_data) const {
EggOptcharUserData *user_data =
DCAST(EggOptcharUserData, joint_data->get_user_data());
if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
// Keep going.
if (joint_data->get_parent() != (EggJointData *)NULL) {
return find_best_vertex_joint(joint_data->get_parent());
}
}
// This is the one!
return joint_data;
@ -419,6 +538,41 @@ apply_user_reparents() {
return did_anything;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::zero_channels
// Access: Private
// Description: Zeroes out the channels specified by the user on the
// command line.
//
// Returns true if any operation was performed, false
// otherwise.
////////////////////////////////////////////////////////////////////
bool EggOptchar::
zero_channels() {
bool did_anything = false;
int num_characters = _collection->get_num_characters();
StringPairs::const_iterator spi;
for (spi = _zero_channels.begin(); spi != _zero_channels.end(); ++spi) {
const StringPair &p = (*spi);
for (int ci = 0; ci < num_characters; ci++) {
EggCharacterData *char_data = _collection->get_character(ci);
EggJointData *joint_data = char_data->find_joint(p._a);
if (joint_data == (EggJointData *)NULL) {
nout << "No joint named " << p._a << " in " << char_data->get_name()
<< ".\n";
} else {
joint_data->zero_channels(p._b);
did_anything = true;
}
}
}
return did_anything;
}
////////////////////////////////////////////////////////////////////
// Function: EggOptchar::analyze_joints
// Access: Private
@ -458,7 +612,7 @@ analyze_joints(EggJointData *joint_data) {
} else {
// This is a second or later matrix.
if (!mat.almost_equal(user_data->_static_mat)) {
if (!mat.almost_equal(user_data->_static_mat, 0.0001)) {
// It's different than the first one.
different_mat = true;
}
@ -472,7 +626,7 @@ analyze_joints(EggJointData *joint_data) {
user_data->_flags |= EggOptcharUserData::F_static;
if (num_mats == 0 ||
user_data->_static_mat.almost_equal(LMatrix4d::ident_mat())) {
user_data->_static_mat.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
// It's not only static, but it's the identity matrix.
user_data->_flags |= EggOptcharUserData::F_identity;
}
@ -531,7 +685,7 @@ analyze_sliders(EggCharacterData *char_data) {
} else {
// This is a second or later value.
if (!IS_NEARLY_EQUAL(value, user_data->_static_value)) {
if (!IS_THRESHOLD_EQUAL(value, user_data->_static_value, 0.0001)) {
// It's different than the first one.
different_value = true;
}
@ -544,7 +698,7 @@ analyze_sliders(EggCharacterData *char_data) {
// All the values are the same for this slider.
user_data->_flags |= EggOptcharUserData::F_static;
if (num_values == 0 || IS_NEARLY_EQUAL(user_data->_static_value, 0.0)) {
if (num_values == 0 || IS_THRESHOLD_ZERO(user_data->_static_value, 0.0001)) {
// It's not only static, but it's the identity value.
user_data->_flags |= EggOptcharUserData::F_identity;
}

View File

@ -51,12 +51,16 @@ protected:
private:
static bool dispatch_vector_string_pair(const string &opt, const string &arg, void *var);
static bool dispatch_name_components(const string &opt, const string &arg, void *var);
void determine_removed_components();
void move_vertices();
bool process_joints();
EggJointData *find_best_parent(EggJointData *joint_data) const;
EggJointData *find_best_vertex_joint(EggJointData *joint_data) const;
bool apply_user_reparents();
bool zero_channels();
void analyze_joints(EggJointData *joint_data);
void analyze_sliders(EggCharacterData *char_data);
void list_joints(EggJointData *joint_data, int indent_level);
@ -80,6 +84,7 @@ private:
};
typedef pvector<StringPair> StringPairs;
StringPairs _reparent_joints;
StringPairs _zero_channels;
vector_string _keep_components;
vector_string _expose_components;

View File

@ -175,8 +175,13 @@ do_reparent() {
// 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";
<< " to ";
if (joint_data->get_parent() == _root_joint) {
nout << "the root";
} else {
nout << joint_data->get_parent()->get_name();
}
nout << " results in a skew transform.\n";
}
}

View File

@ -224,6 +224,31 @@ expose(EggGroup::DCSType dcs_type) {
}
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::zero_channels
// Access: Public
// Description: Calls zero_channels() on all models, and recursively on
// all joints at this node and below.
////////////////////////////////////////////////////////////////////
void EggJointData::
zero_channels(const string &components) {
BackPointers::iterator bpi;
for (bpi = _back_pointers.begin(); bpi != _back_pointers.end(); ++bpi) {
EggBackPointer *back = (*bpi);
if (back != (EggBackPointer *)NULL) {
EggJointPointer *joint;
DCAST_INTO_V(joint, back);
joint->zero_channels(components);
}
}
Children::iterator ci;
for (ci = _children.begin(); ci != _children.end(); ++ci) {
EggJointData *child = (*ci);
child->zero_channels(components);
}
}
////////////////////////////////////////////////////////////////////
// Function: EggJointData::add_back_pointer
// Access: Public, Virtual

View File

@ -53,6 +53,7 @@ public:
bool do_rebuild();
void optimize();
void expose(EggGroup::DCSType dcs_type = EggGroup::DC_default);
void zero_channels(const string &components);
virtual void add_back_pointer(int model_index, EggObject *egg_object);
virtual void write(ostream &out, int indent_level = 0) const;

View File

@ -115,3 +115,13 @@ optimize() {
void EggJointPointer::
expose(EggGroup::DCSType) {
}
////////////////////////////////////////////////////////////////////
// Function: EggJointPointer::zero_channels
// Access: Public, Virtual
// Description: Zeroes out the named components of the transform in
// the animation frames.
////////////////////////////////////////////////////////////////////
void EggJointPointer::
zero_channels(const string &) {
}

View File

@ -52,6 +52,7 @@ public:
virtual void optimize();
virtual void expose(EggGroup::DCSType dcs_type);
virtual void zero_channels(const string &components);
protected:
typedef pvector<LMatrix4d> RebuildFrames;

View File

@ -200,3 +200,27 @@ optimize() {
_xform->optimize();
}
}
////////////////////////////////////////////////////////////////////
// Function: EggMatrixTablePointer::zero_channels
// Access: Public, Virtual
// Description: Zeroes out the named components of the transform in
// the animation frames.
////////////////////////////////////////////////////////////////////
void EggMatrixTablePointer::
zero_channels(const string &components) {
if (_xform == (EggXfmSAnim *)NULL) {
return;
}
// This is particularly easy: we only have to remove children from
// the _xform object whose name is listed in the components.
string::const_iterator si;
for (si = components.begin(); si != components.end(); ++si) {
string table_name(1, *si);
EggNode *child = _xform->find_child(table_name);
if (child != (EggNode *)NULL) {
_xform->remove_child(child);
}
}
}

View File

@ -48,6 +48,7 @@ public:
virtual bool do_rebuild();
virtual void optimize();
virtual void zero_channels(const string &components);
private:
PT(EggTable) _table;