diff --git a/panda/src/linmath/compose_matrix.cxx b/panda/src/linmath/compose_matrix.cxx index d9ac9a616b..6b5dcb84b9 100644 --- a/panda/src/linmath/compose_matrix.cxx +++ b/panda/src/linmath/compose_matrix.cxx @@ -448,7 +448,7 @@ _decompose_matrix(const LMatrix3 &mat, default: linmath_cat.error() - << "Unexpected coordinate system!\n"; + << "Unexpected coordinate system: " << (int)cs << "\n"; return false; } @@ -546,7 +546,7 @@ _decompose_matrix(const LMatrix3 &mat, default: linmath_cat.error() - << "Unexpected coordinate system!\n"; + << "Unexpected coordinate system: " << (int)cs << "\n"; return false; } diff --git a/pandatool/src/eggbase/eggMultiBase.cxx b/pandatool/src/eggbase/eggMultiBase.cxx index 601332e44b..5f8662857d 100644 --- a/pandatool/src/eggbase/eggMultiBase.cxx +++ b/pandatool/src/eggbase/eggMultiBase.cxx @@ -28,6 +28,8 @@ EggMultiBase() { "Force complete loading: load up the egg file along with all of its " "external references.", &EggMultiBase::dispatch_none, &_force_complete); + + _coordinate_system = CS_yup_right; } @@ -118,6 +120,13 @@ read_egg(const Filename &filename) { return (EggData *)NULL; } } + + if (_got_coordinate_system) { + data->set_coordinate_system(_coordinate_system); + } else { + _coordinate_system = data->get_coordinate_system(); + _got_coordinate_system = true; + } return data; } diff --git a/pandatool/src/eggbase/eggMultiFilter.cxx b/pandatool/src/eggbase/eggMultiFilter.cxx index e6b8b4308e..3b126c6fad 100644 --- a/pandatool/src/eggbase/eggMultiFilter.cxx +++ b/pandatool/src/eggbase/eggMultiFilter.cxx @@ -152,7 +152,7 @@ get_output_filename(const Filename &source_filename) const { //////////////////////////////////////////////////////////////////// // Function: EggMultiFilter::write_eggs -// Access: Protected +// Access: Protected, Virtual // Description: Writes out all of the egg files in the _eggs vector, // to the output directory if one is specified, or over // the input files if -inplace was specified. diff --git a/pandatool/src/eggbase/eggMultiFilter.h b/pandatool/src/eggbase/eggMultiFilter.h index 61a978f7eb..c43fbd86e5 100644 --- a/pandatool/src/eggbase/eggMultiFilter.h +++ b/pandatool/src/eggbase/eggMultiFilter.h @@ -25,7 +25,7 @@ protected: virtual bool post_command_line(); Filename get_output_filename(const Filename &source_filename) const; - void write_eggs(); + virtual void write_eggs(); protected: bool _allow_empty; diff --git a/pandatool/src/eggcharbase/eggCharacterCollection.I b/pandatool/src/eggcharbase/eggCharacterCollection.I index da63d5ed4e..fd290a6da1 100644 --- a/pandatool/src/eggcharbase/eggCharacterCollection.I +++ b/pandatool/src/eggcharbase/eggCharacterCollection.I @@ -86,3 +86,16 @@ get_character(int i) const { nassertr(i >= 0 && i < (int)_characters.size(), (EggCharacterData *)NULL); return _characters[i]; } + +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterCollection::get_character_by_model_index +// Access: Public +// Description: Returns the character associated with the indicated +// model index. +//////////////////////////////////////////////////////////////////// +INLINE EggCharacterData *EggCharacterCollection:: +get_character_by_model_index(int model_index) const { + nassertr(model_index >= 0 && model_index < (int)_characters_by_model_index.size(), + (EggCharacterData *)NULL); + return _characters_by_model_index[model_index]; +} diff --git a/pandatool/src/eggcharbase/eggCharacterCollection.cxx b/pandatool/src/eggcharbase/eggCharacterCollection.cxx index c07a4a7e35..d00f936709 100644 --- a/pandatool/src/eggcharbase/eggCharacterCollection.cxx +++ b/pandatool/src/eggcharbase/eggCharacterCollection.cxx @@ -93,6 +93,8 @@ add_egg(EggData *egg) { egg_info._models.push_back(model_root); 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); match_egg_nodes(char_data, root_joint, egg_nodes, egg_index, model_index); @@ -443,6 +445,7 @@ 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); + data->_parent = joint_data; found_egg_match(char_data, data, egg_node, egg_index, model_index); } @@ -547,6 +550,7 @@ 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); + data->_parent = joint_data; found_egg_match(char_data, data, egg_node, egg_index, model_index); } } diff --git a/pandatool/src/eggcharbase/eggCharacterCollection.h b/pandatool/src/eggcharbase/eggCharacterCollection.h index 4a00cebf63..fe96428632 100644 --- a/pandatool/src/eggcharbase/eggCharacterCollection.h +++ b/pandatool/src/eggcharbase/eggCharacterCollection.h @@ -39,6 +39,8 @@ public: INLINE EggCharacterData *get_character(int i) const; EggCharacterData *get_character_by_name(const string &character_name) const; + INLINE EggCharacterData *get_character_by_model_index(int model_index) const; + virtual void write(ostream &out, int indent_level = 0) const; virtual EggCharacterData *make_character_data(); @@ -61,6 +63,7 @@ public: typedef vector Characters; Characters _characters; + Characters _characters_by_model_index; private: bool scan_hierarchy(EggNode *egg_node); diff --git a/pandatool/src/eggcharbase/eggCharacterFilter.cxx b/pandatool/src/eggcharbase/eggCharacterFilter.cxx index b3db5384b7..70e2948d85 100644 --- a/pandatool/src/eggcharbase/eggCharacterFilter.cxx +++ b/pandatool/src/eggcharbase/eggCharacterFilter.cxx @@ -5,6 +5,7 @@ #include "eggCharacterFilter.h" #include "eggCharacterCollection.h" +#include "eggCharacterData.h" //////////////////////////////////////////////////////////////////// @@ -59,6 +60,26 @@ post_command_line() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterFilter::write_eggs +// Access: Protected, Virtual +// Description: Writes out all of the egg files in the _eggs vector, +// to the output directory if one is specified, or over +// the input files if -inplace was specified. +//////////////////////////////////////////////////////////////////// +void EggCharacterFilter:: +write_eggs() { + // Optimize (that is, collapse redudant nodes) in all of the + // characters' joint tables before writing them out. + int num_characters = _collection->get_num_characters(); + for (int i = 0; i < num_characters; i++) { + EggCharacterData *char_data = _collection->get_character(i); + char_data->get_root_joint()->optimize(); + } + + EggMultiFilter::write_eggs(); +} + //////////////////////////////////////////////////////////////////// // Function: EggCharacterFilter::make_collection // Access: Protected, Virtual diff --git a/pandatool/src/eggcharbase/eggCharacterFilter.h b/pandatool/src/eggcharbase/eggCharacterFilter.h index fa1e665c67..d3afec04b7 100644 --- a/pandatool/src/eggcharbase/eggCharacterFilter.h +++ b/pandatool/src/eggcharbase/eggCharacterFilter.h @@ -31,6 +31,7 @@ public: protected: virtual bool post_command_line(); + virtual void write_eggs(); virtual EggCharacterCollection *make_collection(); diff --git a/pandatool/src/eggcharbase/eggJointData.cxx b/pandatool/src/eggcharbase/eggJointData.cxx index 440ae526ee..0fde05cb27 100644 --- a/pandatool/src/eggcharbase/eggJointData.cxx +++ b/pandatool/src/eggcharbase/eggJointData.cxx @@ -22,6 +22,7 @@ EggJointData(EggCharacterCollection *collection, EggCharacterData *char_data) : EggComponentData(collection, char_data) { + _parent = (EggJointData *)NULL; } //////////////////////////////////////////////////////////////////// @@ -30,7 +31,7 @@ EggJointData(EggCharacterCollection *collection, // Description: Returns the first descendent joint found with the // indicated name, or NULL if no joint has that name. //////////////////////////////////////////////////////////////////// -INLINE EggJointData *EggJointData:: +EggJointData *EggJointData:: find_joint(const string &name) { Children::const_iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { @@ -47,6 +48,120 @@ find_joint(const string &name) { return (EggJointData *)NULL; } +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::get_num_frames +// Access: Public +// Description: Returns the number of frames of animation for this +// particular joint in the indicated model. +//////////////////////////////////////////////////////////////////// +int EggJointData:: +get_num_frames(int model_index) const { + EggBackPointer *back = get_model(model_index); + if (back == (EggBackPointer *)NULL) { + return 0; + } + + EggJointPointer *joint; + DCAST_INTO_R(joint, back, 0); + + return joint->get_num_frames(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::get_frame +// Access: Public +// Description: Returns the local transform matrix corresponding to +// this joint position in the nth frame in the indicated +// model. +//////////////////////////////////////////////////////////////////// +LMatrix4d EggJointData:: +get_frame(int model_index, int n) const { + EggBackPointer *back = get_model(model_index); + if (back == (EggBackPointer *)NULL) { + return LMatrix4d::ident_mat(); + } + + EggJointPointer *joint; + DCAST_INTO_R(joint, back, LMatrix4d::ident_mat()); + + return joint->get_frame(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::get_net_frame +// Access: Public +// Description: Returns the complete transform from the root +// corresponding to this joint position in the nth frame +// in the indicated model. +//////////////////////////////////////////////////////////////////// +LMatrix4d EggJointData:: +get_net_frame(int model_index, int n) const { + LMatrix4d mat = get_frame(model_index, n); + if (_parent != (EggJointData *)NULL) { + mat = mat * _parent->get_net_frame(model_index, n); + } + return mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::do_rebuild +// Access: Public +// Description: Calls do_rebuild() on all models, and recursively on +// all joints at this node and below. Returns true if +// all models returned true, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggJointData:: +do_rebuild() { + bool all_ok = true; + + BackPointers::iterator bpi; + for (bpi = _back_pointers.begin(); bpi != _back_pointers.end(); ++bpi) { + EggBackPointer *back = (*bpi); + if (back != (EggBackPointer *)NULL) { + EggJointPointer *joint; + DCAST_INTO_R(joint, back, false); + if (!joint->do_rebuild()) { + all_ok = false; + } + } + } + + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + EggJointData *child = (*ci); + if (!child->do_rebuild()) { + all_ok = false; + } + } + + return all_ok; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::optimize +// Access: Public +// Description: Calls optimize() on all models, and recursively on +// all joints at this node and below. +//////////////////////////////////////////////////////////////////// +void EggJointData:: +optimize() { + 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->optimize(); + } + } + + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + EggJointData *child = (*ci); + child->optimize(); + } +} + //////////////////////////////////////////////////////////////////// // Function: EggJointData::add_back_pointer // Access: Public, Virtual diff --git a/pandatool/src/eggcharbase/eggJointData.h b/pandatool/src/eggcharbase/eggJointData.h index eb0783058d..fcfa5b5f46 100644 --- a/pandatool/src/eggcharbase/eggJointData.h +++ b/pandatool/src/eggcharbase/eggJointData.h @@ -10,6 +10,8 @@ #include "eggComponentData.h" +#include + //////////////////////////////////////////////////////////////////// // Class : EggJointData // Description : This is one node of a hierarchy of EggJointData @@ -27,12 +29,20 @@ public: INLINE EggJointData *get_child(int n) const; EggJointData *find_joint(const string &name); + 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; + + 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: typedef vector Children; Children _children; + EggJointData *_parent; friend class EggCharacterCollection; }; diff --git a/pandatool/src/eggcharbase/eggJointNodePointer.cxx b/pandatool/src/eggcharbase/eggJointNodePointer.cxx index 015faba721..4bcf9e6e18 100644 --- a/pandatool/src/eggcharbase/eggJointNodePointer.cxx +++ b/pandatool/src/eggcharbase/eggJointNodePointer.cxx @@ -79,3 +79,48 @@ set_frame(int n, const LMatrix4d &mat) { nassertv(n == 0); _joint->set_transform(mat); } + +//////////////////////////////////////////////////////////////////// +// Function: EggJointNodePointer::add_rebuild_frame +// Access: Public, Virtual +// Description: Adds a new frame to the set of rebuild frames. See +// begin_rebuild() and do_rebuild(). Returns true if +// this is valid, false otherwise (e.g. adding multiple +// frames to a static joint). +//////////////////////////////////////////////////////////////////// +bool EggJointNodePointer:: +add_rebuild_frame(const LMatrix4d &mat) { + if (!_rebuild_frames.empty()) { + // Only one frame may be added to a . + return false; + } + return EggJointPointer::add_rebuild_frame(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointNodePointer::do_rebuild +// Access: Public, Virtual +// Description: Rebuilds the entire table all at once, based on the +// frames added by repeated calls to add_rebuild_frame() +// since the last call to begin_rebuild(). +// +// Until do_rebuild() is called, the animation table is +// not changed. +// +// The return value is true if all frames are +// acceptable, or false if there is some problem. +//////////////////////////////////////////////////////////////////// +bool EggJointNodePointer:: +do_rebuild() { + if (_rebuild_frames.empty()) { + return true; + } + + if (_rebuild_frames.size() != 1) { + return false; + } + + _joint->set_transform(_rebuild_frames[0]); + _rebuild_frames.clear(); + return true; +} diff --git a/pandatool/src/eggcharbase/eggJointNodePointer.h b/pandatool/src/eggcharbase/eggJointNodePointer.h index f0f2b2144c..e743f6e46b 100644 --- a/pandatool/src/eggcharbase/eggJointNodePointer.h +++ b/pandatool/src/eggcharbase/eggJointNodePointer.h @@ -25,6 +25,9 @@ public: virtual LMatrix4d get_frame(int n) const; virtual void set_frame(int n, const LMatrix4d &mat); + virtual bool add_rebuild_frame(const LMatrix4d &mat); + virtual bool do_rebuild(); + private: PT(EggGroup) _joint; diff --git a/pandatool/src/eggcharbase/eggJointPointer.cxx b/pandatool/src/eggcharbase/eggJointPointer.cxx index df79ed4d9c..f50582da70 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.cxx +++ b/pandatool/src/eggcharbase/eggJointPointer.cxx @@ -20,3 +20,65 @@ bool EggJointPointer:: add_frame(const LMatrix4d &) { return false; } + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::begin_rebuild +// Access: Public +// Description: Resets the set of rebuild frames in preparation for +// rebuilding the complete table of frames. Repeated +// calls to add_rebuild_frame() will build up the frames +// without changing the values returned by get_frame(); +// the table will eventually be updated when do_rebuild +// is called. +//////////////////////////////////////////////////////////////////// +void EggJointPointer:: +begin_rebuild() { + _rebuild_frames.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::add_rebuild_frame +// Access: Public, Virtual +// Description: Adds a new frame to the set of rebuild frames. See +// begin_rebuild() and do_rebuild(). Returns true if +// this is valid, false otherwise (e.g. adding multiple +// frames to a static joint). +//////////////////////////////////////////////////////////////////// +bool EggJointPointer:: +add_rebuild_frame(const LMatrix4d &mat) { + _rebuild_frames.push_back(mat); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::do_rebuild +// Access: Public, Virtual +// Description: Rebuilds the entire table all at once, based on the +// frames added by repeated calls to add_rebuild_frame() +// since the last call to begin_rebuild(). +// +// Until do_rebuild() is called, the animation table is +// not changed. +// +// The return value is true if all frames are +// acceptable, or false if there is some problem. +//////////////////////////////////////////////////////////////////// +bool EggJointPointer:: +do_rebuild() { + if (_rebuild_frames.empty()) { + return true; + } + _rebuild_frames.clear(); + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::optimize +// Access: Public, Virtual +// Description: Resets the table before writing to disk so that +// redundant rows (e.g. i { 1 1 1 1 1 1 1 1 }) are +// collapsed out. +//////////////////////////////////////////////////////////////////// +void EggJointPointer:: +optimize() { +} diff --git a/pandatool/src/eggcharbase/eggJointPointer.h b/pandatool/src/eggcharbase/eggJointPointer.h index b6e4064a3e..b0a05a56a0 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.h +++ b/pandatool/src/eggcharbase/eggJointPointer.h @@ -28,6 +28,16 @@ public: virtual void set_frame(int n, const LMatrix4d &mat)=0; virtual bool add_frame(const LMatrix4d &mat); + void begin_rebuild(); + virtual bool add_rebuild_frame(const LMatrix4d &mat); + virtual bool do_rebuild(); + + virtual void optimize(); + +protected: + typedef vector RebuildFrames; + RebuildFrames _rebuild_frames; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx index 82468dfeff..d0bb034f57 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx @@ -29,6 +29,7 @@ EggMatrixTablePointer(EggObject *object) { if (child->get_name() == "xform") { if (child->is_of_type(EggXfmSAnim::get_class_type())) { _xform = DCAST(EggXfmSAnim, child); + _xform->normalize(); found = true; } else if (child->is_of_type(EggXfmAnimData::get_class_type())) { @@ -67,6 +68,12 @@ get_num_frames() const { //////////////////////////////////////////////////////////////////// LMatrix4d EggMatrixTablePointer:: get_frame(int n) const { + if (get_num_frames() == 1) { + // If we have exactly one frame, then we have as many frames as we + // want; just repeat the first frame. + n = 0; + } + nassertr(n >= 0 && n < get_num_frames(), LMatrix4d::ident_mat()); LMatrix4d mat; _xform->get_value(n, mat); @@ -82,7 +89,7 @@ get_frame(int n) const { void EggMatrixTablePointer:: set_frame(int n, const LMatrix4d &mat) { nassertv(n >= 0 && n < get_num_frames()); - // Do something here. + _xform->set_value(n, mat); } //////////////////////////////////////////////////////////////////// @@ -94,6 +101,58 @@ set_frame(int n, const LMatrix4d &mat) { //////////////////////////////////////////////////////////////////// bool EggMatrixTablePointer:: add_frame(const LMatrix4d &mat) { - // Do something here. + if (_xform == (EggXfmSAnim *)NULL) { + return false; + } + + return _xform->add_data(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMatrixTablePointer::do_rebuild +// Access: Public, Virtual +// Description: Rebuilds the entire table all at once, based on the +// frames added by repeated calls to add_rebuild_frame() +// since the last call to begin_rebuild(). +// +// Until do_rebuild() is called, the animation table is +// not changed. +// +// The return value is true if all frames are +// acceptable, or false if there is some problem. +//////////////////////////////////////////////////////////////////// +bool EggMatrixTablePointer:: +do_rebuild() { + if (_rebuild_frames.empty()) { + return true; + } + + if (_xform == (EggXfmSAnim *)NULL) { + return false; + } + + _xform->clear_data(); + RebuildFrames::const_iterator fi; + for (fi = _rebuild_frames.begin(); fi != _rebuild_frames.end(); ++fi) { + if (!_xform->add_data(*fi)) { + return false; + } + } + + _rebuild_frames.clear(); return true; } + +//////////////////////////////////////////////////////////////////// +// Function: EggMatrixTablePointer::optimize +// Access: Public, Virtual +// Description: Resets the table before writing to disk so that +// redundant rows (e.g. i { 1 1 1 1 1 1 1 1 }) are +// collapsed out. +//////////////////////////////////////////////////////////////////// +void EggMatrixTablePointer:: +optimize() { + if (_xform != (EggXfmSAnim *)NULL) { + _xform->optimize(); + } +} diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.h b/pandatool/src/eggcharbase/eggMatrixTablePointer.h index 834bb25e41..7d621b5f33 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.h +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.h @@ -30,6 +30,10 @@ public: virtual void set_frame(int n, const LMatrix4d &mat); virtual bool add_frame(const LMatrix4d &mat); + virtual bool do_rebuild(); + + virtual void optimize(); + private: PT(EggTable) _table; PT(EggXfmSAnim) _xform; diff --git a/pandatool/src/eggprogs/Sources.pp b/pandatool/src/eggprogs/Sources.pp index 036b889c2a..23e631bf32 100644 --- a/pandatool/src/eggprogs/Sources.pp +++ b/pandatool/src/eggprogs/Sources.pp @@ -21,11 +21,11 @@ #end bin_target -#begin test_bin_target +#begin bin_target #define LOCAL_LIBS eggcharbase $[LOCAL_LIBS] #define TARGET egg-topstrip #define SOURCES \ eggTopstrip.cxx eggTopstrip.h -#end test_bin_target +#end bin_target diff --git a/pandatool/src/eggprogs/eggTopstrip.cxx b/pandatool/src/eggprogs/eggTopstrip.cxx index 7a48dc7ab7..89e1da0725 100644 --- a/pandatool/src/eggprogs/eggTopstrip.cxx +++ b/pandatool/src/eggprogs/eggTopstrip.cxx @@ -10,6 +10,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////// // Function: EggTopstrip::Constructor @@ -43,7 +44,7 @@ EggTopstrip() { ("n", "", 0, "Do not invert the matrix before applying. This causes an " "additive effect.", - &EggTopstrip::dispatch_true, &_got_invert_transform, &_invert_transform); + &EggTopstrip::dispatch_false, &_got_invert_transform, &_invert_transform); add_option ("s", "[ijkphrxyz]", 0, @@ -73,13 +74,15 @@ void EggTopstrip:: run() { nassertv(_collection != (EggCharacterCollection *)NULL); nassertv(_collection->get_num_eggs() > 0); + + check_transform_channels(); // Get the number of characters first, in case adding the // _channel_egg changes this. int num_characters = _collection->get_num_characters(); - // Determine which model we'll be pulling the animation channels - // from. + // Determine which model and character we'll be pulling the + // animation channels from. int from_model = -1; if (!_channel_filename.empty()) { @@ -108,27 +111,38 @@ run() { // Now process each character. for (int i = 0; i < num_characters; i++) { EggCharacterData *char_data = _collection->get_character(i); + nout << "Processing " << char_data->get_name() << "\n"; EggJointData *root_joint = char_data->get_root_joint(); + // We'll read the transform to apply from this character, which + // will be the same character unless -r was specified. + EggCharacterData *from_char = char_data; + if (from_model != -1) { + from_char = _collection->get_character_by_model_index(from_model); + } + + // Determine which joint we'll use to extract the transform to + // apply. EggJointData *top_joint = (EggJointData *)NULL; if (_top_joint_name.empty()) { // The default top joint name is the alphabetically first joint // in the top level. if (root_joint->get_num_children() == 0) { - nout << "Character " << char_data->get_name() << " has no joints.\n"; + nout << "Character " << from_char->get_name() << " has no joints.\n"; exit(1); } top_joint = root_joint->get_child(0); } else { - top_joint = char_data->find_joint(_top_joint_name); + top_joint = from_char->find_joint(_top_joint_name); if (top_joint == (EggJointData *)NULL) { - nout << "Character " << char_data->get_name() + nout << "Character " << from_char->get_name() << " has no joint named " << _top_joint_name << "\n"; exit(1); } } + // First, transform all the joints. int num_children = root_joint->get_num_children(); for (int i = 0; i < num_children; i++) { EggJointData *joint_data = root_joint->get_child(i); @@ -141,14 +155,59 @@ run() { for (int m = 0; m < num_models; m++) { EggNode *node = char_data->get_model_root(m); if (!node->is_of_type(EggTable::get_class_type())) { - strip_anim_vertices(node, m, from_model, top_joint); + strip_anim_vertices(node, char_data->get_model_index(m), + from_model, top_joint); } } } + // Now, trigger the actual rebuilding of all the joint data. + for (int i = 0; i < num_characters; i++) { + EggCharacterData *char_data = _collection->get_character(i); + char_data->get_root_joint()->do_rebuild(); + } + write_eggs(); } +//////////////////////////////////////////////////////////////////// +// Function: EggTopstrip::check_transform_channels +// Access: Public +// Description: Checks the _transform_channels string to ensure that +// it contains only the expected nine letters, or a +// subset. +//////////////////////////////////////////////////////////////////// +void EggTopstrip:: +check_transform_channels() { + static string expected = "ijkphrxyz"; + static const int num_channels = 9; + bool has_each[num_channels]; + memset(has_each, 0, num_channels * sizeof(bool)); + + for (size_t p = 0; p < _transform_channels.size(); p++) { + int i = expected.find(_transform_channels[p]); + if (i == (int)string::npos) { + nout << "Invalid letter for -s: " << _transform_channels[p] << "\n"; + exit(1); + } + nassertv(i < num_channels); + has_each[i] = true; + } + + _transform_channels = ""; + for (int i = 0; i < num_channels; i++) { + if (has_each[i]) { + _transform_channels += expected[i]; + } + } + + if (_transform_channels.empty()) { + nout << "No transform specified for -s.\n"; + exit(1); + } +} + + //////////////////////////////////////////////////////////////////// // Function: EggTopstrip::strip_anim // Access: Public @@ -160,47 +219,42 @@ void EggTopstrip:: strip_anim(EggJointData *joint_data, int from_model, EggJointData *top_joint) { int num_models = joint_data->get_num_models(); for (int i = 0; i < num_models; i++) { - EggBackPointer *back = joint_data->get_model(i); - if (back != (EggBackPointer *)NULL) { + int model = (from_model < 0) ? i : from_model; + + if (joint_data->has_model(i)) { + if (!top_joint->has_model(model)) { + nout << "Warning: Joint " << top_joint->get_name() + << " is not defined in all models.\n"; + return; + } + + int num_into_frames = joint_data->get_num_frames(i); + int num_from_frames = top_joint->get_num_frames(model); + + int num_frames = max(num_into_frames, num_from_frames); + + EggBackPointer *back = joint_data->get_model(i); + nassertv(back != (EggBackPointer *)NULL); EggJointPointer *joint; DCAST_INTO_V(joint, back); - - cerr << "joint is " << joint->get_type() << "\n"; - - int model = (from_model < 0) ? i : from_model; - EggBackPointer *from_back = top_joint->get_model(model); - if (from_back == (EggBackPointer *)NULL) { - nout << "Joint " << top_joint->get_name() << " has no model index " - << model << "\n"; - exit(1); - } - EggJointPointer *from_joint; - DCAST_INTO_V(from_joint, from_back); - - int num_into_frames = joint->get_num_frames(); - int num_from_frames = from_joint->get_num_frames(); - - int num_frames = max(num_into_frames, num_from_frames); - - for (int f = 0; f < num_frames; f++) { - LMatrix4d start = joint->get_frame(f % num_into_frames); - LMatrix4d strip = from_joint->get_frame(f % num_from_frames); - - if (_invert_transform) { - strip.invert_in_place(); - } - - cerr << "Applying " << strip << " to " << f << " of " - << joint_data->get_name() << " model " << i << "\n"; - - if (f >= num_into_frames) { - if (!joint->add_frame(start * strip)) { - nout << "Cannot apply multiple frames of animation to a model file.\n" - << "In general, be careful when using -r and model files.\n"; - exit(1); - } - } else { - joint->set_frame(f, start * strip); + + // Compute and apply the new transforms. + joint->begin_rebuild(); + + int f; + for (f = 0; f < num_frames; f++) { + LMatrix4d into = joint_data->get_frame(i, f % num_into_frames); + LMatrix4d from = top_joint->get_net_frame(model, f % num_from_frames); + + adjust_transform(from); + + if (!joint->add_rebuild_frame(into * from)) { + nout << + "Cannot apply multiple frames of animation to a model file.\n" + "In general, -r cannot be used when a model file is being " + "adjusted, unless the named source is a one-frame animation " + "file, or another model file.\n"; + exit(1); } } } @@ -217,23 +271,81 @@ void EggTopstrip:: strip_anim_vertices(EggNode *egg_node, int into_model, int from_model, EggJointData *top_joint) { int model = (from_model < 0) ? into_model : from_model; - EggBackPointer *from_back = top_joint->get_model(model); - if (from_back == (EggBackPointer *)NULL) { - nout << "Joint " << top_joint->get_name() << " has no model index " - << model << "\n"; - exit(1); + if (!top_joint->has_model(model)) { + nout << "Warning: Joint " << top_joint->get_name() + << " is not defined in all models.\n"; + return; } - EggJointPointer *from_joint; - DCAST_INTO_V(from_joint, from_back); + LMatrix4d from = top_joint->get_net_frame(model, 0); + adjust_transform(from); - LMatrix4d strip = from_joint->get_frame(0); + egg_node->transform_vertices_only(from); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggTopstrip::adjust_transform +// Access: Public +// Description: Adjust the transform extracted from the "top" joint +// according to the -s and -i/-n options, prior to +// applying it to the skeleton. +//////////////////////////////////////////////////////////////////// +void EggTopstrip:: +adjust_transform(LMatrix4d &mat) const { + if (_transform_channels.length() != 9) { + // Decompose and recompose the matrix, so we can eliminate the + // parts the user doesn't want. + + LVecBase3d scale, hpr, translate; + bool result = decompose_matrix(mat, scale, hpr, translate, _coordinate_system); + if (!result) { + nout << "Warning: skew transform in animation.\n"; + } else { + LVecBase3d new_scale(1.0, 1.0, 1.0); + LVecBase3d new_hpr(0.0, 0.0, 0.0); + LVecBase3d new_translate(0.0, 0.0, 0.0); + + for (size_t i = 0; i < _transform_channels.size(); i++) { + switch (_transform_channels[i]) { + case 'i': + new_scale[0] = scale[0]; + break; + case 'j': + new_scale[1] = scale[1]; + break; + case 'k': + new_scale[2] = scale[2]; + break; + + case 'h': + new_hpr[0] = hpr[0]; + break; + case 'p': + new_hpr[1] = hpr[1]; + break; + case 'r': + new_hpr[2] = hpr[2]; + break; + + case 'x': + new_translate[0] = translate[0]; + break; + case 'y': + new_translate[1] = translate[1]; + break; + case 'z': + new_translate[2] = translate[2]; + break; + } + } + + compose_matrix(mat, new_scale, new_hpr, new_translate, _coordinate_system); + } + } if (_invert_transform) { - strip.invert_in_place(); + mat.invert_in_place(); } - - cerr << "Applying " << strip << " to vertices.\n"; - egg_node->transform_vertices_only(strip); } diff --git a/pandatool/src/eggprogs/eggTopstrip.h b/pandatool/src/eggprogs/eggTopstrip.h index a38e93a877..2350e84620 100644 --- a/pandatool/src/eggprogs/eggTopstrip.h +++ b/pandatool/src/eggprogs/eggTopstrip.h @@ -29,12 +29,15 @@ public: EggTopstrip(); void run(); + void check_transform_channels(); void strip_anim(EggJointData *joint_data, int from_model, EggJointData *top_joint); void strip_anim_vertices(EggNode *egg_node, int into_model, int from_model, EggJointData *top_joint); + void adjust_transform(LMatrix4d &mat) const; + string _top_joint_name; bool _got_invert_transform;