diff --git a/pandatool/src/egg-optchar/eggOptchar.cxx b/pandatool/src/egg-optchar/eggOptchar.cxx index 7000ae74f8..d984db9982 100644 --- a/pandatool/src/egg-optchar/eggOptchar.cxx +++ b/pandatool/src/egg-optchar/eggOptchar.cxx @@ -149,11 +149,24 @@ EggOptchar() { add_option ("q", "quantum", 0, "Quantize joint membership values to the given unit. This is " - "the smallest significant change in joint membership. The " - "default is 0.01; specifying 0 means to preserve the original " - "values.", + "the smallest significant change in joint membership. There can " + "be a significant performance (and memory utilization) runtime " + "benefit for eliminating small differences in joint memberships " + "between neighboring vertices. The default is 0.01; specifying " + "0 means to preserve the original values.", &EggOptchar::dispatch_double, NULL, &_vref_quantum); + add_option + ("qa", "quantum[,hprxyzijkabc]", 0, + "Quantizes animation channels to the given unit. This rounds each " + "of the named components of all joints to the nearest multiple of unit. " + "There is no performance benefit, and little compression benefit, " + "for doing this; and this may introduce visible artifacts to the " + "animation. However, sometimes it is a useful tool for animation " + "analysis and comparison. This option may be repeated several times " + "to quantize different channels by a different amount.", + &EggOptchar::dispatch_double_components, NULL, &_quantize_anims); + _optimal_hierarchy = false; _vref_quantum = 0.01; } @@ -218,15 +231,18 @@ run() { do_reparent(); } + // We currently do not implement optimizing morph sliders. Need + // to add this at some point; it's quite easy. Identity and empty + // morph sliders can simply be removed, while static sliders need + // to be applied to the vertices and then removed. + // Quantize the vertex memberships. We call this even if // _vref_quantum is 0, because this also normalizes the vertex // memberships. quantize_vertices(); - // We currently do not implement optimizing morph sliders. Need - // to add this at some point; it's quite easy. Identity and empty - // morph sliders can simply be removed, while static sliders need - // to be applied to the vertices and then removed. + // Also quantize the animation channels, if the user so requested. + quantize_channels(); // Finally, flag all the groups as the user requested. if (!_flag_groups.empty()) { @@ -336,6 +352,63 @@ dispatch_name_components(const string &opt, const string &arg, void *var) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: ProgramBase::dispatch_double_components +// Access: Protected, Static +// Description: Accepts a double value optionally followed by a comma +// and some of the nine standard component letters, +// +// The data pointer is to a DoubleStrings vector; the +// pair will be pushed onto the end of the vector. +//////////////////////////////////////////////////////////////////// +bool EggOptchar:: +dispatch_double_components(const string &opt, const string &arg, void *var) { + DoubleStrings *ip = (DoubleStrings *)var; + + vector_string words; + tokenize(arg, words, ","); + + bool valid_double = false; + + DoubleString sp; + if (words.size() == 1) { + valid_double = string_to_double(words[0], sp._a); + + } else if (words.size() == 2) { + valid_double = string_to_double(words[0], sp._a); + sp._b = words[1]; + + } else { + nout << "-" << opt + << " requires a numeric value followed by a string.\n"; + return false; + } + + if (!valid_double) { + nout << "-" << opt + << " requires a numeric value followed by a string.\n"; + return false; + } + + if (sp._b.empty()) { + sp._b = matrix_component_letters; + } else { + for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) { + if (strchr(matrix_component_letters, *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_component_letters << "\".\n"; + return false; + } + } + } + + ip->push_back(sp); + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: ProgramBase::dispatch_flag_groups // Access: Protected, Static @@ -775,6 +848,38 @@ zero_channels() { return did_anything; } +//////////////////////////////////////////////////////////////////// +// Function: EggOptchar::quantize_channels +// Access: Private +// Description: Quantizes the channels specified by the user on the +// command line. +// +// Returns true if any operation was performed, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool EggOptchar:: +quantize_channels() { + bool did_anything = false; + int num_characters = _collection->get_num_characters(); + + DoubleStrings::const_iterator spi; + for (spi = _quantize_anims.begin(); spi != _quantize_anims.end(); ++spi) { + const DoubleString &p = (*spi); + + for (int ci = 0; ci < num_characters; ci++) { + EggCharacterData *char_data = _collection->get_character(ci); + EggJointData *joint_data = char_data->get_root_joint(); + + if (joint_data != (EggJointData *)NULL) { + joint_data->quantize_channels(p._b, p._a); + did_anything = true; + } + } + } + + return did_anything; +} + //////////////////////////////////////////////////////////////////// // Function: EggOptchar::analyze_joints // Access: Private diff --git a/pandatool/src/egg-optchar/eggOptchar.h b/pandatool/src/egg-optchar/eggOptchar.h index 8ca204a991..7d488e0293 100644 --- a/pandatool/src/egg-optchar/eggOptchar.h +++ b/pandatool/src/egg-optchar/eggOptchar.h @@ -54,6 +54,7 @@ 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); + static bool dispatch_double_components(const string &opt, const string &arg, void *var); static bool dispatch_flag_groups(const string &opt, const string &arg, void *var); void determine_removed_components(); @@ -64,6 +65,7 @@ private: bool apply_user_reparents(); bool zero_channels(); + bool quantize_channels(); void analyze_joints(EggJointData *joint_data, int level); void analyze_sliders(EggCharacterData *char_data); void list_joints(EggJointData *joint_data, int indent_level, bool verbose); @@ -99,6 +101,14 @@ private: vector_string _drop_components; vector_string _expose_components; + class DoubleString { + public: + double _a; + string _b; + }; + typedef pvector DoubleStrings; + DoubleStrings _quantize_anims; + typedef pvector Globs; class FlagGroupsEntry { diff --git a/pandatool/src/eggcharbase/eggJointData.cxx b/pandatool/src/eggcharbase/eggJointData.cxx index d84184a1ae..850304e837 100644 --- a/pandatool/src/eggcharbase/eggJointData.cxx +++ b/pandatool/src/eggcharbase/eggJointData.cxx @@ -400,6 +400,31 @@ zero_channels(const string &components) { } } +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::quantize_channels +// Access: Public +// Description: Calls quantize_channels() on all models for this joint, +// and then recurses downwards to all joints below. +//////////////////////////////////////////////////////////////////// +void EggJointData:: +quantize_channels(const string &components, double quantum) { + 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->quantize_channels(components, quantum); + } + } + + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + EggJointData *child = (*ci); + child->quantize_channels(components, quantum); + } +} + //////////////////////////////////////////////////////////////////// // Function: EggJointData::add_back_pointer // Access: Public, Virtual diff --git a/pandatool/src/eggcharbase/eggJointData.h b/pandatool/src/eggcharbase/eggJointData.h index 7f362eff6f..14febaa0e5 100644 --- a/pandatool/src/eggcharbase/eggJointData.h +++ b/pandatool/src/eggcharbase/eggJointData.h @@ -61,6 +61,7 @@ public: void optimize(); void expose(EggGroup::DCSType dcs_type = EggGroup::DC_default); void zero_channels(const string &components); + void quantize_channels(const string &components, double quantum); 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 07bb9beebf..721c95e173 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.cxx +++ b/pandatool/src/eggcharbase/eggJointPointer.cxx @@ -125,3 +125,13 @@ expose(EggGroup::DCSType) { void EggJointPointer:: zero_channels(const string &) { } + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::quantize_channels +// Access: Public, Virtual +// Description: Rounds the named components of the transform to the +// nearest multiple of quantum. +//////////////////////////////////////////////////////////////////// +void EggJointPointer:: +quantize_channels(const string &, double) { +} diff --git a/pandatool/src/eggcharbase/eggJointPointer.h b/pandatool/src/eggcharbase/eggJointPointer.h index 2dfb848ba1..c6d6e79bb6 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.h +++ b/pandatool/src/eggcharbase/eggJointPointer.h @@ -63,6 +63,7 @@ public: virtual void optimize(); virtual void expose(EggGroup::DCSType dcs_type); virtual void zero_channels(const string &components); + virtual void quantize_channels(const string &components, double quantum); virtual EggJointPointer *make_new_joint(const string &name)=0; diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx index bab2af0fce..5ea37d78b6 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx @@ -250,6 +250,32 @@ zero_channels(const string &components) { } } +//////////////////////////////////////////////////////////////////// +// Function: EggMatrixTablePointer::quantize_channels +// Access: Public, Virtual +// Description: Rounds the named components of the transform to the +// nearest multiple of quantum. +//////////////////////////////////////////////////////////////////// +void EggMatrixTablePointer:: +quantize_channels(const string &components, double quantum) { + if (_xform == (EggXfmSAnim *)NULL) { + return; + } + + // This is similar to the above: we quantize children of 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 && + child->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *anim = DCAST(EggSAnimData, child); + anim->quantize(quantum); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: EggMatrixTablePointer::make_new_joint // Access: Public, Virtual diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.h b/pandatool/src/eggcharbase/eggMatrixTablePointer.h index 36176d38e8..10bd826b5b 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.h +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.h @@ -50,6 +50,7 @@ public: virtual void optimize(); virtual void zero_channels(const string &components); + virtual void quantize_channels(const string &components, double quantum); virtual EggJointPointer *make_new_joint(const string &name);