Browse Source

handle some broken egg files more gracefully

David Rose 22 years ago
parent
commit
c9e4bbc9c0

+ 11 - 0
pandatool/src/eggcharbase/eggBackPointer.cxx

@@ -30,6 +30,17 @@ EggBackPointer::
 EggBackPointer() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggBackPointer::extend_to
+//       Access: Public, Virtual
+//  Description: Extends the table to the indicated number of frames.
+////////////////////////////////////////////////////////////////////
+void EggBackPointer::
+extend_to(int num_frames) {
+  // Whoops, can't extend this kind of table!
+  nassertv(false);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggBackPointer::has_vertices
 //       Access: Public, Virtual

+ 2 - 0
pandatool/src/eggcharbase/eggBackPointer.h

@@ -40,6 +40,8 @@ class EggBackPointer : public TypedObject {
 public:
   EggBackPointer();
 
+  virtual int get_num_frames() const=0;
+  virtual void extend_to(int num_frames);
   virtual bool has_vertices() const;
 
 public:

+ 40 - 1
pandatool/src/eggcharbase/eggCharacterCollection.cxx

@@ -106,7 +106,7 @@ add_egg(EggData *egg) {
       }
       egg_info._models.push_back(model_root);
 
-      char_data->add_model(model_index, model_root);
+      char_data->add_model(model_index, model_root, egg);
       nassertr(model_index == (int)_characters_by_model_index.size(), -1);
       _characters_by_model_index.push_back(char_data);
       root_joint->add_back_pointer(model_index, desc._root_node);
@@ -663,3 +663,42 @@ write(ostream &out, int indent_level) const {
     char_data->write(out, indent_level);
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCharacterCollection::check_errors
+//       Access: Public
+//  Description: Can be called after the collection has been
+//               completely filled up with egg files to output any
+//               messages from warning conditions that have been
+//               detected, such as inconsistent animation tables.
+//
+//               In addition to reporting this errors, calling this
+//               function will also ensure that they are all repaired
+//               (although certain kinds of errors will be repaired
+//               regardless).
+////////////////////////////////////////////////////////////////////
+void EggCharacterCollection::
+check_errors(ostream &out) {
+  Characters::const_iterator ci;
+  for (ci = _characters.begin(); ci != _characters.end(); ++ci) {
+    EggCharacterData *char_data = (*ci);
+    int num_joints = char_data->get_num_joints();
+    for (int j = 0; j < num_joints; j++) {
+      EggJointData *joint_data = char_data->get_joint(j);
+      if (joint_data->_forced_rest_frames_equal) {
+        out << "Warning: rest frames for " << joint_data->get_name() 
+            << " were different.\n";
+      }
+    }
+
+    int num_models = char_data->get_num_models();
+    for (int mi = 0; mi < num_models; mi++) {
+      int model_index = char_data->get_model_index(mi);
+      if (!char_data->check_num_frames(model_index)) {
+        out << "Warning: animation from " 
+            << char_data->get_egg_data(model_index)->get_egg_filename().get_basename()
+            << " had an inconsistent number of frames.\n";
+      }
+    }
+  }
+}

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

@@ -55,6 +55,7 @@ public:
   INLINE EggCharacterData *get_character_by_model_index(int model_index) const;
 
   virtual void write(ostream &out, int indent_level = 0) const;
+  void check_errors(ostream &out);
 
   virtual EggCharacterData *make_character_data();
   virtual EggJointData *make_joint_data(EggCharacterData *char_data);

+ 13 - 0
pandatool/src/eggcharbase/eggCharacterData.I

@@ -68,6 +68,19 @@ get_model_root(int n) const {
   return _models[n]._model_root;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggCharacterData::get_egg_data
+//       Access: Public
+//  Description: Returns the EggData representing the egg file that
+//               defined this particular model.  Note that one egg
+//               file might contain multiple models.
+////////////////////////////////////////////////////////////////////
+INLINE EggData *EggCharacterData::
+get_egg_data(int n) const {
+  nassertr(n >= 0 && n < (int)_models.size(), (EggData *)NULL);
+  return _models[n]._egg_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggCharacterData::get_root_joint
 //       Access: Public

+ 45 - 1
pandatool/src/eggcharbase/eggCharacterData.cxx

@@ -65,10 +65,11 @@ EggCharacterData::
 //               file: in either case, a hierarchy of joints.
 ////////////////////////////////////////////////////////////////////
 void EggCharacterData::
-add_model(int model_index, EggNode *model_root) {
+add_model(int model_index, EggNode *model_root, EggData *egg_data) {
   Model m;
   m._model_index = model_index;
   m._model_root = model_root;
+  m._egg_data = egg_data;
   _models.push_back(m);
 }
 
@@ -102,6 +103,49 @@ get_num_frames(int model_index) const {
   return max_num_frames;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggCharacterData::check_num_frames
+//       Access: Public
+//  Description: Walks through each component and ensures that all
+//               have the same number of frames of animation (except
+//               for those that contain 0 or 1 frames, of course).
+//               Returns true if all are valid, false if there is a
+//               discreprency (in which case the shorter component are
+//               extended).
+////////////////////////////////////////////////////////////////////
+bool EggCharacterData::
+check_num_frames(int model_index) {
+  int max_num_frames = 0;
+  bool any_violations = false;
+  Components::const_iterator ci;
+  for (ci = _components.begin(); ci != _components.end(); ++ci) {
+    EggComponentData *component = (*ci);
+    int num_frames = component->get_num_frames(model_index);
+    if (num_frames > 1 && max_num_frames > 1 && 
+        max_num_frames != num_frames) {
+      // If we have two different opinions about the number of frames
+      // (other than 0 or 1), we have a discrepency.  This is an error
+      // condition.
+      any_violations = true;
+    }
+    max_num_frames = max(max_num_frames, num_frames);
+  }
+
+  if (any_violations) {
+    // Now go back through and force all components to the appropriate
+    // length.
+    for (ci = _components.begin(); ci != _components.end(); ++ci) {
+      EggComponentData *component = (*ci);
+      int num_frames = component->get_num_frames(model_index);
+      if (num_frames > 1 && max_num_frames != num_frames) {
+        component->extend_to(model_index, max_num_frames);
+      }
+    }
+  }
+
+  return !any_violations;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggCharacterData::do_reparent
 //       Access: Public

+ 5 - 1
pandatool/src/eggcharbase/eggCharacterData.h

@@ -24,6 +24,7 @@
 #include "eggJointData.h"
 
 #include "eggNode.h"
+#include "eggData.h"
 #include "pointerTo.h"
 #include "namable.h"
 
@@ -64,11 +65,13 @@ public:
   EggCharacterData(EggCharacterCollection *collection);
   virtual ~EggCharacterData();
 
-  void add_model(int model_index, EggNode *model_root);
+  void add_model(int model_index, EggNode *model_root, EggData *egg_data);
   INLINE int get_num_models() const;
   INLINE int get_model_index(int n) const;
   INLINE EggNode *get_model_root(int n) const;
+  INLINE EggData *get_egg_data(int n) const;
   int get_num_frames(int model_index) const;
+  bool check_num_frames(int model_index);
 
   INLINE EggJointData *get_root_joint() const;
   INLINE EggJointData *find_joint(const string &name) const;
@@ -91,6 +94,7 @@ protected:
   public:
     int _model_index;
     PT(EggNode) _model_root;
+    PT(EggData) _egg_data;
   };
   typedef pvector<Model> Models;
   Models _models;

+ 2 - 0
pandatool/src/eggcharbase/eggCharacterFilter.cxx

@@ -70,6 +70,8 @@ post_command_line() {
     }
   }
 
+  _collection->check_errors(nout);
+
   return true;
 }
 

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

@@ -19,7 +19,7 @@
 #ifndef EGGCHARACTERFILTER_H
 #define EGGCHARACTERFILTER_H
 
-#include <pandatoolbase.h>
+#include "pandatoolbase.h"
 
 #include "eggMultiFilter.h"
 

+ 29 - 0
pandatool/src/eggcharbase/eggComponentData.cxx

@@ -82,6 +82,35 @@ matches_name(const string &name) const {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggComponentData::get_num_frames
+//       Access: Public, Virtual
+//  Description: Returns the number of frames of animation for this
+//               particular component in the indicated model.
+////////////////////////////////////////////////////////////////////
+int EggComponentData::
+get_num_frames(int model_index) const {
+  EggBackPointer *back = get_model(model_index);
+  if (back == (EggBackPointer *)NULL) {
+    return 0;
+  }
+  return back->get_num_frames();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggComponentData::extend_to
+//       Access: Public, Virtual
+//  Description: Extends the number of frames in the indicated model
+//               (presumably an animation table model) to the given
+//               number.
+////////////////////////////////////////////////////////////////////
+void EggComponentData::
+extend_to(int model_index, int num_frames) const {
+  EggBackPointer *back = get_model(model_index);
+  nassertv(back != (EggBackPointer *)NULL);
+  return back->extend_to(num_frames);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggComponentData::set_model
 //       Access: Public

+ 2 - 1
pandatool/src/eggcharbase/eggComponentData.h

@@ -45,7 +45,8 @@ public:
   void add_name(const string &name);
   bool matches_name(const string &name) const;
 
-  virtual int get_num_frames(int model_index) const=0;
+  int get_num_frames(int model_index) const;
+  void extend_to(int model_index, int num_frames) const;
 
   virtual void add_back_pointer(int model_index, EggObject *egg_object)=0;
   virtual void write(ostream &out, int indent_level = 0) const=0;

+ 26 - 0
pandatool/src/eggcharbase/eggJointData.I

@@ -48,6 +48,32 @@ get_child(int n) const {
   return _children[n];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggJointData::has_rest_frame
+//       Access: Public
+//  Description: Returns true if the joint knows its rest frame, false
+//               otherwise.  In general, this will be true as long as
+//               the joint is included in at least one model file, or
+//               false if it appears only in animation files.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggJointData::
+has_rest_frame() const {
+  return _has_rest_frame;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggJointData::get_rest_frame
+//       Access: Public
+//  Description: Returns the rest frame of the joint.  This is the
+//               matrix value that appears for the joint in each model
+//               file; it should be the same transform in each model.
+////////////////////////////////////////////////////////////////////
+INLINE const LMatrix4d &EggJointData::
+get_rest_frame() const {
+  nassertr(has_rest_frame(), LMatrix4d::ident_mat());
+  return _rest_frame;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggJointData::reparent_to
 //       Access: Public

+ 26 - 19
pandatool/src/eggcharbase/eggJointData.cxx

@@ -40,6 +40,8 @@ EggJointData(EggCharacterCollection *collection,
 {
   _parent = (EggJointData *)NULL;
   _new_parent = (EggJointData *)NULL;
+  _has_rest_frame = false;
+  _forced_rest_frames_equal = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -65,25 +67,6 @@ find_joint(const string &name) {
   return (EggJointData *)NULL;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggJointData::get_num_frames
-//       Access: Public, Virtual
-//  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
@@ -262,6 +245,30 @@ add_back_pointer(int model_index, EggObject *egg_object) {
     // It must be a <Joint>.
     EggJointNodePointer *joint = new EggJointNodePointer(egg_object);
     set_model(model_index, joint);
+    if (!_has_rest_frame) {
+      _rest_frame = joint->get_frame(0);
+      _has_rest_frame = true;
+
+    } else {
+      // If this new node doesn't come within an acceptable tolerance
+      // of our first reading of this joint's rest frame, set a
+      // warning flag.
+      if (!_rest_frame.almost_equal(joint->get_frame(0), 0.001)) {
+        _forced_rest_frames_equal = true;
+      }
+
+      // In any case, ensure the rest frames are exactly equal.
+
+      // Actually, this may not be a good idea; it is, in fact, valid
+      // (although unusual) for different models to have different
+      // rest frames, provided their vertices appriopriately reflect
+      // the different rest frames.  But we do have at least one
+      // character, Mickey, for which some of the lower-level LOD's
+      // have different rest frames that are in fact completely wrong,
+      // so forcing them all to the same value is correct in that
+      // case.  Maybe this should be a command-line option.
+      joint->set_frame(0, _rest_frame);
+    }
 
   } else if (egg_object->is_of_type(EggTable::get_class_type())) {
     // It's a <Table> with an "xform" child beneath it.

+ 7 - 1
pandatool/src/eggcharbase/eggJointData.h

@@ -43,10 +43,12 @@ public:
   INLINE EggJointData *get_child(int n) const;
   EggJointData *find_joint(const string &name);
 
-  virtual 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;
 
+  INLINE bool has_rest_frame() const;
+  INLINE const LMatrix4d &get_rest_frame() const;
+
   INLINE void reparent_to(EggJointData *new_parent);
   void move_vertices_to(EggJointData *new_owner);
 
@@ -69,6 +71,10 @@ private:
   const LMatrix4d &get_new_net_frame_inv(int model_index, int n);
   LMatrix4d get_new_frame(int model_index, int n);
 
+  bool _has_rest_frame;
+  bool _forced_rest_frames_equal;
+  LMatrix4d _rest_frame;
+
   // These are used to cache the above results for optimizing
   // do_compute_reparent().
   LMatrix4d _new_net_frame, _new_net_frame_inv;

+ 22 - 0
pandatool/src/eggcharbase/eggMatrixTablePointer.cxx

@@ -74,6 +74,28 @@ get_num_frames() const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggMatrixTablePointer::extend_to
+//       Access: Public, Virtual
+//  Description: Extends the table to the indicated number of frames.
+////////////////////////////////////////////////////////////////////
+void EggMatrixTablePointer::
+extend_to(int num_frames) {
+  nassertv(_xform != (EggXfmSAnim *)NULL);
+  _xform->normalize();
+  int num_rows = _xform->get_num_rows();
+  LMatrix4d last_mat;
+  if (num_rows == 0) {
+    last_mat = LMatrix4d::ident_mat();
+  } else {
+    _xform->get_value(num_rows - 1, last_mat);
+  }
+
+  while (num_rows < num_frames) {
+    _xform->add_data(last_mat);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggMatrixTablePointer::get_frame
 //       Access: Public, Virtual

+ 2 - 1
pandatool/src/eggcharbase/eggMatrixTablePointer.h

@@ -38,7 +38,8 @@ class EggMatrixTablePointer : public EggJointPointer {
 public:
   EggMatrixTablePointer(EggObject *object);
 
-  virtual int get_num_frames() const;
+  virtual int get_num_frames() const; 
+  virtual void extend_to(int num_frames);
   virtual LMatrix4d get_frame(int n) const;
   virtual void set_frame(int n, const LMatrix4d &mat);
   virtual bool add_frame(const LMatrix4d &mat);

+ 21 - 0
pandatool/src/eggcharbase/eggScalarTablePointer.cxx

@@ -47,6 +47,27 @@ get_num_frames() const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggScalarTablePointer::extend_to
+//       Access: Public, Virtual
+//  Description: Extends the table to the indicated number of frames.
+////////////////////////////////////////////////////////////////////
+void EggScalarTablePointer::
+extend_to(int num_frames) {
+  nassertv(_data != (EggSAnimData *)NULL);
+  int num_rows = _data->get_num_rows();
+  double last_value;
+  if (num_rows == 0) {
+    last_value = 0.0;
+  } else {
+    last_value = _data->get_value(num_rows - 1);
+  }
+
+  while (num_rows < num_frames) {
+    _data->add_data(last_value);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggScalarTablePointer::get_frame
 //       Access: Public, Virtual

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

@@ -38,6 +38,7 @@ public:
   EggScalarTablePointer(EggObject *object);
 
   virtual int get_num_frames() const;
+  virtual void extend_to(int num_frames);
   virtual double get_frame(int n) const;
 
 private:

+ 0 - 19
pandatool/src/eggcharbase/eggSliderData.cxx

@@ -40,25 +40,6 @@ EggSliderData(EggCharacterCollection *collection,
 {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggSliderData::get_num_frames
-//       Access: Public, Virtual
-//  Description: Returns the number of frames of animation for this
-//               particular slider in the indicated model.
-////////////////////////////////////////////////////////////////////
-int EggSliderData::
-get_num_frames(int model_index) const {
-  EggBackPointer *back = get_model(model_index);
-  if (back == (EggBackPointer *)NULL) {
-    return 0;
-  }
-
-  EggSliderPointer *slider;
-  DCAST_INTO_R(slider, back, 0);
-
-  return slider->get_num_frames();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: EggSliderData::get_frame
 //       Access: Public

+ 1 - 2
pandatool/src/eggcharbase/eggSliderData.h

@@ -19,7 +19,7 @@
 #ifndef EGGSLIDERDATA_H
 #define EGGSLIDERDATA_H
 
-#include <pandatoolbase.h>
+#include "pandatoolbase.h"
 
 #include "eggComponentData.h"
 
@@ -37,7 +37,6 @@ public:
   EggSliderData(EggCharacterCollection *collection,
                 EggCharacterData *char_data);
 
-  virtual int get_num_frames(int model_index) const;
   double get_frame(int model_index, int n) const;
 
   virtual void add_back_pointer(int model_index, EggObject *egg_object);