Browse Source

*** empty log message ***

David Rose 25 years ago
parent
commit
07709159f0

+ 2 - 2
panda/src/linmath/compose_matrix.cxx

@@ -448,7 +448,7 @@ _decompose_matrix(const LMatrix3<NumType> &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<NumType> &mat,
 
   default:
     linmath_cat.error()
-      << "Unexpected coordinate system!\n";
+      << "Unexpected coordinate system: " << (int)cs << "\n";
     return false;
   }
 

+ 9 - 0
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;
 }

+ 1 - 1
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.

+ 1 - 1
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;

+ 13 - 0
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];
+}

+ 4 - 0
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);
 	}
       }

+ 3 - 0
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<EggCharacterData *> Characters;
   Characters _characters;
+  Characters _characters_by_model_index;
 
 private:
   bool scan_hierarchy(EggNode *egg_node);

+ 21 - 0
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

+ 1 - 0
pandatool/src/eggcharbase/eggCharacterFilter.h

@@ -31,6 +31,7 @@ public:
 
 protected:
   virtual bool post_command_line();
+  virtual void write_eggs();
 
   virtual EggCharacterCollection *make_collection();
 

+ 116 - 1
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

+ 10 - 0
pandatool/src/eggcharbase/eggJointData.h

@@ -10,6 +10,8 @@
 
 #include "eggComponentData.h"
 
+#include <luse.h>
+
 ////////////////////////////////////////////////////////////////////
 // 	 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<EggJointData *> Children;
   Children _children;
+  EggJointData *_parent;
 
   friend class EggCharacterCollection;
 };

+ 45 - 0
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 <Joint>.
+    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;
+}

+ 3 - 0
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;
 

+ 62 - 0
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() {
+}

+ 10 - 0
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<LMatrix4d> RebuildFrames;
+  RebuildFrames _rebuild_frames;
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 61 - 2
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();
+  }
+}

+ 4 - 0
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;

+ 2 - 2
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

+ 168 - 56
pandatool/src/eggprogs/eggTopstrip.cxx

@@ -10,6 +10,7 @@
 #include <eggCharacterData.h>
 #include <eggJointPointer.h>
 #include <eggTable.h>
+#include <compose_matrix.h>
 
 ////////////////////////////////////////////////////////////////////
 //     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) {
-      EggJointPointer *joint;
-      DCAST_INTO_V(joint, back);
+    int model = (from_model < 0) ? i : from_model;
 
-      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);
+    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;
       }
-      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_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);
-
-      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);
+      
+      EggBackPointer *back = joint_data->get_model(i);
+      nassertv(back != (EggBackPointer *)NULL);
+      EggJointPointer *joint;
+      DCAST_INTO_V(joint, back);
+      
+      // 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);
+
+  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;
+	}
+      }
 
-  LMatrix4d strip = from_joint->get_frame(0);
+      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);
 }
 
 

+ 3 - 0
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;