From 6b7baa43d7430c21d1705086c9bc73ef6470c8e4 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 22 Jul 2003 01:50:40 +0000 Subject: [PATCH] add -zero, etc. --- pandatool/src/egg-optchar/eggOptchar.cxx | 182 ++++++++++++++++-- pandatool/src/egg-optchar/eggOptchar.h | 5 + .../src/eggcharbase/eggCharacterData.cxx | 9 +- pandatool/src/eggcharbase/eggJointData.cxx | 25 +++ pandatool/src/eggcharbase/eggJointData.h | 1 + pandatool/src/eggcharbase/eggJointPointer.cxx | 10 + pandatool/src/eggcharbase/eggJointPointer.h | 1 + .../src/eggcharbase/eggMatrixTablePointer.cxx | 24 +++ .../src/eggcharbase/eggMatrixTablePointer.h | 1 + 9 files changed, 242 insertions(+), 16 deletions(-) diff --git a/pandatool/src/egg-optchar/eggOptchar.cxx b/pandatool/src/egg-optchar/eggOptchar.cxx index e4873a9d05..347c0bd05a 100644 --- a/pandatool/src/egg-optchar/eggOptchar.cxx +++ b/pandatool/src/egg-optchar/eggOptchar.cxx @@ -30,6 +30,7 @@ #include "string_utils.h" #include "dcast.h" #include "pset.h" +#include "compose_matrix.h" #include @@ -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,8 +466,32 @@ 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); - return find_best_parent(joint_data->get_parent()); + 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! @@ -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; } diff --git a/pandatool/src/egg-optchar/eggOptchar.h b/pandatool/src/egg-optchar/eggOptchar.h index 1a556009cc..20ba0a1ed4 100644 --- a/pandatool/src/egg-optchar/eggOptchar.h +++ b/pandatool/src/egg-optchar/eggOptchar.h @@ -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 StringPairs; StringPairs _reparent_joints; + StringPairs _zero_channels; vector_string _keep_components; vector_string _expose_components; diff --git a/pandatool/src/eggcharbase/eggCharacterData.cxx b/pandatool/src/eggcharbase/eggCharacterData.cxx index 25e9ef383e..1788eff12b 100644 --- a/pandatool/src/eggcharbase/eggCharacterData.cxx +++ b/pandatool/src/eggcharbase/eggCharacterData.cxx @@ -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"; } } diff --git a/pandatool/src/eggcharbase/eggJointData.cxx b/pandatool/src/eggcharbase/eggJointData.cxx index 82f5ac13cd..93bff10443 100644 --- a/pandatool/src/eggcharbase/eggJointData.cxx +++ b/pandatool/src/eggcharbase/eggJointData.cxx @@ -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 diff --git a/pandatool/src/eggcharbase/eggJointData.h b/pandatool/src/eggcharbase/eggJointData.h index 4819dd4164..8955e7e758 100644 --- a/pandatool/src/eggcharbase/eggJointData.h +++ b/pandatool/src/eggcharbase/eggJointData.h @@ -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; diff --git a/pandatool/src/eggcharbase/eggJointPointer.cxx b/pandatool/src/eggcharbase/eggJointPointer.cxx index f4aaade8b0..32111659a7 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.cxx +++ b/pandatool/src/eggcharbase/eggJointPointer.cxx @@ -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 &) { +} diff --git a/pandatool/src/eggcharbase/eggJointPointer.h b/pandatool/src/eggcharbase/eggJointPointer.h index 95814b3566..6454d686e7 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.h +++ b/pandatool/src/eggcharbase/eggJointPointer.h @@ -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 RebuildFrames; diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx index 5c078902df..05d3eb7281 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx @@ -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); + } + } +} diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.h b/pandatool/src/eggcharbase/eggMatrixTablePointer.h index 01afab1a9a..f3470b3c77 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.h +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.h @@ -48,6 +48,7 @@ public: virtual bool do_rebuild(); virtual void optimize(); + virtual void zero_channels(const string &components); private: PT(EggTable) _table;