Browse Source

fix morphs in character flattening; better support for flattening animated + unanimated

David Rose 19 years ago
parent
commit
e43f4f5c64

+ 11 - 8
panda/src/egg2pg/eggLoader.cxx

@@ -90,6 +90,8 @@
 #include "configVariableString.h"
 #include "transformBlendTable.h"
 #include "transformBlend.h"
+#include "sparseArray.h"
+#include "bitArray.h"
 
 #include <ctype.h>
 #include <algorithm>
@@ -2094,6 +2096,7 @@ make_vertex_data(const EggRenderState *render_state,
     temp_format->set_animation(animation);
 
     blend_table = new TransformBlendTable;
+    blend_table->set_rows(SparseArray::lower_on(vertex_pool->size()));
 
     PT(GeomVertexArrayFormat) anim_array_format = new GeomVertexArrayFormat;
     anim_array_format->add_column
@@ -2101,21 +2104,21 @@ make_vertex_data(const EggRenderState *render_state,
        Geom::NT_uint16, Geom::C_index);
     temp_format->add_array(anim_array_format);
 
-    pset<string> slider_names;
+    pmap<string, BitArray> slider_names;
     EggVertexPool::const_iterator vi;
     for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
       EggVertex *vertex = (*vi);
       
       EggMorphVertexList::const_iterator mvi;
       for (mvi = vertex->_dxyzs.begin(); mvi != vertex->_dxyzs.end(); ++mvi) {
-        slider_names.insert((*mvi).get_name());
+        slider_names[(*mvi).get_name()].set_bit(vertex->get_index());
         record_morph(anim_array_format, character_maker, (*mvi).get_name(),
                      InternalName::get_vertex(), 3);
       }
       if (vertex->has_normal()) {
         EggMorphNormalList::const_iterator mni;
         for (mni = vertex->_dnormals.begin(); mni != vertex->_dnormals.end(); ++mni) {
-          slider_names.insert((*mni).get_name());
+          slider_names[(*mni).get_name()].set_bit(vertex->get_index());
           record_morph(anim_array_format, character_maker, (*mni).get_name(),
                        InternalName::get_normal(), 3);
         }
@@ -2123,7 +2126,7 @@ make_vertex_data(const EggRenderState *render_state,
       if (has_colors && vertex->has_color()) {
         EggMorphColorList::const_iterator mci;
         for (mci = vertex->_drgbas.begin(); mci != vertex->_drgbas.end(); ++mci) {
-          slider_names.insert((*mci).get_name());
+          slider_names[(*mci).get_name()].set_bit(vertex->get_index());
           record_morph(anim_array_format, character_maker, (*mci).get_name(),
                        InternalName::get_color(), 4);
         }
@@ -2137,7 +2140,7 @@ make_vertex_data(const EggRenderState *render_state,
 
         EggMorphTexCoordList::const_iterator mti;
         for (mti = egg_uv->_duvs.begin(); mti != egg_uv->_duvs.end(); ++mti) {
-          slider_names.insert((*mti).get_name());
+          slider_names[(*mti).get_name()].set_bit(vertex->get_index());
           record_morph(anim_array_format, character_maker, (*mti).get_name(),
                        iname, has_w ? 3 : 2);
         }
@@ -2149,10 +2152,10 @@ make_vertex_data(const EggRenderState *render_state,
 
       slider_table = new SliderTable;
       
-      pset<string>::iterator si;
+      pmap<string, BitArray>::iterator si;
       for (si = slider_names.begin(); si != slider_names.end(); ++si) {
-        VertexSlider *slider = character_maker->egg_to_slider(*si);
-        slider_table->add_slider(slider);
+        PT(VertexSlider) slider = character_maker->egg_to_slider((*si).first);
+        slider_table->add_slider(slider, (*si).second);
       }
     }
 

+ 123 - 54
panda/src/gobj/geomVertexData.cxx

@@ -1244,44 +1244,76 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) {
     int num_morphs = orig_format->get_num_morphs();
     for (int mi = 0; mi < num_morphs; mi++) {
       CPT(InternalName) slider_name = orig_format->get_morph_slider(mi);
-      const VertexSlider *slider = slider_table->find_slider(slider_name);
-      if (slider != (VertexSlider *)NULL) {
-        float slider_value = slider->get_slider();
-        if (slider_value != 0.0f) {
-          CPT(InternalName) base_name = orig_format->get_morph_base(mi);
-          CPT(InternalName) delta_name = orig_format->get_morph_delta(mi);
-          
-          GeomVertexRewriter data(new_data, base_name);
-          GeomVertexReader delta(this, delta_name);
-
-          if (data.get_column()->get_num_values() == 4) {
-            if (data.get_column()->has_homogeneous_coord()) {
-              for (int i = 0; i < num_rows; i++) {
-                // Scale the delta by the homogeneous coordinate.
-                LPoint4f vertex = data.get_data4f();
-                LPoint3f d = delta.get_data3f();
-                d *= slider_value * vertex[3];
-                data.set_data4f(vertex[0] + d[0],
-                                vertex[1] + d[1],
-                                vertex[2] + d[2],
-                                vertex[3]);
-              }
-            } else {
-              // Just apply the four-component delta.
-              for (int i = 0; i < num_rows; i++) {
-                const LPoint4f &vertex = data.get_data4f();
-                LPoint4f d = delta.get_data4f();
-                data.set_data4f(vertex + d * slider_value);
+
+      const SparseArray &sliders = slider_table->find_sliders(slider_name);
+      if (!sliders.is_zero()) {
+        nassertv(!sliders.is_inverse());
+        int num_slider_subranges = sliders.get_num_subranges();
+        for (int sni = 0; sni < num_slider_subranges; ++sni) {
+          int slider_begin = sliders.get_subrange_begin(sni);
+          int slider_end = sliders.get_subrange_end(sni);
+          for (int sn = slider_begin; sn < slider_end; ++sn) {
+            const VertexSlider *slider = slider_table->get_slider(sn);
+            const SparseArray &rows = slider_table->get_slider_rows(sn);
+            nassertv(!rows.is_inverse());
+
+            float slider_value = slider->get_slider();
+            if (slider_value != 0.0f) {
+              CPT(InternalName) base_name = orig_format->get_morph_base(mi);
+              CPT(InternalName) delta_name = orig_format->get_morph_delta(mi);
+              
+              GeomVertexRewriter data(new_data, base_name);
+              GeomVertexReader delta(this, delta_name);
+              int num_subranges = rows.get_num_subranges();
+
+              if (data.get_column()->get_num_values() == 4) {
+                if (data.get_column()->has_homogeneous_coord()) {
+                  // Scale the delta by the homogeneous coordinate.
+                  for (int i = 0; i < num_subranges; ++i) {
+                    int begin = rows.get_subrange_begin(i);
+                    int end = rows.get_subrange_end(i);
+                    data.set_row(begin);
+                    delta.set_row(begin);
+                    for (int j = begin; j < end; ++j) {
+                      LPoint4f vertex = data.get_data4f();
+                      LPoint3f d = delta.get_data3f();
+                      d *= slider_value * vertex[3];
+                      data.set_data4f(vertex[0] + d[0],
+                                      vertex[1] + d[1],
+                                      vertex[2] + d[2],
+                                      vertex[3]);
+                    }
+                  }
+                } else {
+                  // Just apply the four-component delta.
+                  for (int i = 0; i < num_subranges; ++i) {
+                    int begin = rows.get_subrange_begin(i);
+                    int end = rows.get_subrange_end(i);
+                    data.set_row(begin);
+                    delta.set_row(begin);
+                    for (int j = begin; j < end; ++j) {
+                      const LPoint4f &vertex = data.get_data4f();
+                      LPoint4f d = delta.get_data4f();
+                      data.set_data4f(vertex + d * slider_value);
+                    }
+                  }
+                }
+              } else {
+                // 3-component or smaller values; don't worry about a
+                // homogeneous coordinate.
+                for (int i = 0; i < num_subranges; ++i) {
+                  int begin = rows.get_subrange_begin(i);
+                  int end = rows.get_subrange_end(i);
+                  data.set_row(begin);
+                  delta.set_row(begin);
+                  for (int j = begin; j < end; ++j) {
+                    const LPoint3f &vertex = data.get_data3f();
+                    LPoint3f d = delta.get_data3f();
+                    data.set_data3f(vertex + d * slider_value);
+                  }
+                }
               }
             }
-          } else {
-            // 3-component or smaller values; don't worry about a
-            // homogeneous coordinate.
-            for (int i = 0; i < num_rows; i++) {
-              const LPoint3f &vertex = data.get_data3f();
-              LPoint3f d = delta.get_data3f();
-              data.set_data3f(vertex + d * slider_value);
-            }
           }
         }
       }
@@ -1310,36 +1342,54 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) {
       return;
     }
 
+    const SparseArray &rows = tb_table->get_rows();
+    int num_subranges = rows.get_num_subranges();
+
     int ci;
     for (ci = 0; ci < orig_format->get_num_points(); ci++) {
       GeomVertexRewriter data(new_data, orig_format->get_point(ci));
-      blendi.set_row(0);
       
       if (data.get_column()->get_num_values() == 4) {
-        for (int i = 0; i < num_rows; i++) {
-          LPoint4f vertex = data.get_data4f();
-          int bi = blendi.get_data1i();
-          tb_table->get_blend(bi).transform_point(vertex, current_thread);
-          data.set_data4f(vertex);
+        for (int i = 0; i < num_subranges; ++i) {
+          int begin = rows.get_subrange_begin(i);
+          int end = rows.get_subrange_end(i);
+          data.set_row(begin);
+          blendi.set_row(begin);
+          for (int j = begin; j < end; ++j) {
+            LPoint4f vertex = data.get_data4f();
+            int bi = blendi.get_data1i();
+            tb_table->get_blend(bi).transform_point(vertex, current_thread);
+            data.set_data4f(vertex);
+          }
         }
       } else {
-        for (int i = 0; i < num_rows; i++) {
-          LPoint3f vertex = data.get_data3f();
-          int bi = blendi.get_data1i();
-          tb_table->get_blend(bi).transform_point(vertex, current_thread);
-          data.set_data3f(vertex);
+        for (int i = 0; i < num_subranges; ++i) {
+          int begin = rows.get_subrange_begin(i);
+          int end = rows.get_subrange_end(i);
+          data.set_row(begin);
+          blendi.set_row(begin);
+          for (int j = begin; j < end; ++j) {
+            LPoint3f vertex = data.get_data3f();
+            int bi = blendi.get_data1i();
+            tb_table->get_blend(bi).transform_point(vertex, current_thread);
+            data.set_data3f(vertex);
+          }
         }
       }
     }
     for (ci = 0; ci < orig_format->get_num_vectors(); ci++) {
       GeomVertexRewriter data(new_data, orig_format->get_vector(ci));
-      blendi.set_row(0);
-      
-      for (int i = 0; i < num_rows; i++) {
-        LVector3f vertex = data.get_data3f();
-        int bi = blendi.get_data1i();
-        tb_table->get_blend(bi).transform_vector(vertex, current_thread);
-        data.set_data3f(vertex);
+      for (int i = 0; i < num_subranges; ++i) {
+        int begin = rows.get_subrange_begin(i);
+        int end = rows.get_subrange_end(i);
+        data.set_row(begin);
+        blendi.set_row(begin);
+        for (int j = begin; j < end; ++j) {
+          LVector3f vertex = data.get_data3f();
+          int bi = blendi.get_data1i();
+          tb_table->get_blend(bi).transform_vector(vertex, current_thread);
+          data.set_data3f(vertex);
+        }
       }
     }
   }
@@ -1564,6 +1614,25 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 
   _modified = Geom::get_next_modified();
 
+  if (!_arrays.empty() && manager->get_file_minor_ver() < 7) {
+    // Bam files prior to 6.7 did not store a SparseArray in the
+    // SliderTable or TransformBlendTable entries.  We need to make up
+    // a SparseArray for each of them that reflects the complete
+    // number of rows in the data.
+    SparseArray all_rows;
+    all_rows.set_range(0, _arrays[0]->get_num_rows());
+
+    if (_slider_table != (SliderTable *)NULL) {
+      int num_sliders = _slider_table->get_num_sliders();
+      for (int i = 0; i < num_sliders; ++i) {
+        ((SliderTable *)_slider_table.p())->set_slider_rows(i, all_rows);
+      }
+    }
+    if (_transform_blend_table != (TransformBlendTable *)NULL) {
+      _transform_blend_table->set_rows(all_rows);
+    }
+  }
+
   return pi;
 }
 

+ 23 - 10
panda/src/gobj/sliderTable.I

@@ -79,34 +79,47 @@ get_num_sliders() const {
 INLINE const VertexSlider *SliderTable::
 get_slider(int n) const {
   nassertr(n >= 0 && n < (int)_sliders.size(), NULL);
-  return _sliders[n];
+  return _sliders[n]._slider;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: SliderTable::find_slider
+//     Function: SliderTable::get_slider_rows
 //       Access: Published
-//  Description: Returns the slider with the indicated name, or NULL
-//               if no slider in the table has that name.
+//  Description: Returns the set of rows (vertices) governed by the
+//               nth slider in the table.
 ////////////////////////////////////////////////////////////////////
-INLINE const VertexSlider *SliderTable::
-find_slider(const InternalName *name) const {
+INLINE const SparseArray &SliderTable::
+get_slider_rows(int n) const {
+  nassertr(n >= 0 && n < (int)_sliders.size(), _empty_array);
+  return _sliders[n]._rows;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::find_sliders
+//       Access: Published
+//  Description: Returns a list of slider indices that represent the
+//               list of sliders with the indicated name, or an empty
+//               SparseArray if no slider in the table has that name.
+////////////////////////////////////////////////////////////////////
+INLINE const SparseArray &SliderTable::
+find_sliders(const InternalName *name) const {
   SlidersByName::const_iterator sni;
   sni = _sliders_by_name.find(name);
   if (sni != _sliders_by_name.end()) {
     return (*sni).second;
   }
-  return NULL;
+  return _empty_array;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SliderTable::has_slider
 //       Access: Published
-//  Description: Returns true if the table has a slider by the
-//               indicated name, false otherwise.
+//  Description: Returns true if the table has at least one slider by
+//               the indicated name, false otherwise.
 ////////////////////////////////////////////////////////////////////
 INLINE bool SliderTable::
 has_slider(const InternalName *name) const {
-  return (find_slider(name) != (VertexSlider *)NULL);
+  return (!find_sliders(name).is_zero());
 }
 
 ////////////////////////////////////////////////////////////////////

+ 49 - 39
panda/src/gobj/sliderTable.cxx

@@ -21,6 +21,7 @@
 #include "bamWriter.h"
 #include "vertexTransform.h"
 
+SparseArray SliderTable::_empty_array;
 TypeHandle SliderTable::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -82,13 +83,29 @@ set_slider(int n, const VertexSlider *slider) {
   nassertv(!_is_registered);
   nassertv(n >= 0 && n < (int)_sliders.size());
 
-  if (_sliders[n]->get_name() != slider->get_name()) {
-    // Not allowed to move a slider this way.
-    nassertv(!has_slider(slider->get_name()));
+  if (_sliders[n]._slider->get_name() != slider->get_name()) {
+    _sliders_by_name[_sliders[n]._slider->get_name()].clear_bit(n);
+    _sliders_by_name[slider->get_name()].set_bit(n);
   }
 
-  _sliders[n] = slider;
-  _sliders_by_name[slider->get_name()] = slider;
+  _sliders[n]._slider = slider;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::set_slider_rows
+//       Access: Published
+//  Description: Replaces the rows affected by the nth slider.  Only
+//               valid for unregistered tables.
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+set_slider_rows(int n, const SparseArray &rows) {
+  // We don't actually enforce the registration requirement, since
+  // gee, it doesn't actually matter here; and the GeomVertexData
+  // needs to be able to change the SparseArrays in the bam reader.
+  //  nassertv(!_is_registered);
+  nassertv(n >= 0 && n < (int)_sliders.size());
+
+  _sliders[n]._rows = rows;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -102,41 +119,28 @@ remove_slider(int n) {
   nassertv(!_is_registered);
   nassertv(n >= 0 && n < (int)_sliders.size());
 
-  SlidersByName::iterator si = _sliders_by_name.find(_sliders[n]->get_name());
-  nassertv(si != _sliders_by_name.end());
-  _sliders_by_name.erase(si);
-
+  _sliders_by_name[_sliders[n]._slider->get_name()].clear_bit(n);
   _sliders.erase(_sliders.begin() + n);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SliderTable::add_slider
 //       Access: Published
-//  Description: Adds a new slider to the table, or replaces an
-//               existing slider with the same name, and returns the
+//  Description: Adds a new slider to the table, and returns the
 //               index number of the new slider.  Only valid for
 //               unregistered tables.
 ////////////////////////////////////////////////////////////////////
 int SliderTable::
-add_slider(const VertexSlider *slider) {
+add_slider(const VertexSlider *slider, const SparseArray &rows) {
   nassertr(!_is_registered, -1);
 
-  SlidersByName::iterator sni = _sliders_by_name.find(slider->get_name());
-  if (sni != _sliders_by_name.end()) {
-    // We've already got a slider with this name; replace it.
-    CPT(VertexSlider) orig_slider = (*sni).second;
-    Sliders::iterator si = find(_sliders.begin(), _sliders.end(), orig_slider);
-    nassertr(si != _sliders.end(), -1);
-    (*si) = slider;
-    (*sni).second = slider;
-    
-    return si - _sliders.begin();
-  }
-
-  // This is the first slider with this name.
   int new_index = (int)_sliders.size();
-  _sliders.push_back(slider);
-  _sliders_by_name[slider->get_name()] = slider;
+
+  SliderDef slider_def;
+  slider_def._slider = slider;
+  slider_def._rows = rows;
+  _sliders.push_back(slider_def);
+  _sliders_by_name[slider->get_name()].set_bit(new_index);
 
   return new_index;
 }
@@ -149,7 +153,8 @@ add_slider(const VertexSlider *slider) {
 void SliderTable::
 write(ostream &out) const {
   for (size_t i = 0; i < _sliders.size(); ++i) {
-    out << i << ". " << *_sliders[i] << "\n";
+    out << i << ". " << *_sliders[i]._slider << " " 
+        << _sliders[i]._rows << "\n";
   }
 }
 
@@ -164,7 +169,7 @@ do_register() {
 
   Sliders::iterator si;
   for (si = _sliders.begin(); si != _sliders.end(); ++si) {
-    const VertexSlider *slider = (*si);
+    const VertexSlider *slider = (*si)._slider;
     bool inserted = ((VertexSlider *)slider)->_tables.insert(this).second;
     nassertv(inserted);
   }
@@ -183,7 +188,7 @@ do_unregister() {
 
   Sliders::iterator si;
   for (si = _sliders.begin(); si != _sliders.end(); ++si) {
-    const VertexSlider *slider = (*si);
+    const VertexSlider *slider = (*si)._slider;
     ((VertexSlider *)slider)->_tables.erase(this);
   }
   _is_registered = false;
@@ -213,8 +218,9 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   dg.add_uint16(_sliders.size());
   Sliders::const_iterator si;
   for (si = _sliders.begin(); si != _sliders.end(); ++si) {
-    manager->write_pointer(dg, (*si)->get_name());
-    manager->write_pointer(dg, (*si));
+    manager->write_pointer(dg, (*si)._slider->get_name());
+    manager->write_pointer(dg, (*si)._slider);
+    (*si)._rows.write_datagram(manager, dg);
   }
 
   manager->write_cdata(dg, _cycler);
@@ -231,15 +237,12 @@ int SliderTable::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
 
-  Sliders::iterator si;
-  for (si = _sliders.begin(); si != _sliders.end(); ++si) {
+  for (size_t n = 0; n < _sliders.size(); ++n) {
     CPT(InternalName) name = DCAST(InternalName, p_list[pi++]);
     PT(VertexSlider) slider = DCAST(VertexSlider, p_list[pi++]);
 
-    (*si) = slider;
-
-    bool inserted = _sliders_by_name.insert(SlidersByName::value_type(name, slider.p())).second;
-    nassertr(inserted, pi);
+    _sliders[n]._slider = slider;
+    _sliders_by_name[name].set_bit(n);
   }
 
   return pi;
@@ -281,7 +284,14 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   for (size_t i = 0; i < num_sliders; ++i) {
     manager->read_pointer(scan);
     manager->read_pointer(scan);
-    _sliders.push_back(NULL);
+    _sliders.push_back(SliderDef());
+    if (manager->get_file_minor_ver() >= 7) {
+      _sliders[i]._rows.read_datagram(scan, manager);
+    } else {
+      // In this case, for bam files prior to 6.7, we must define the
+      // SparseArray with the full number of vertices.  This is done
+      // in GeomVertexData::complete_pointers().
+    }
   }
 
   manager->read_cdata(scan, _cycler);

+ 15 - 5
panda/src/gobj/sliderTable.h

@@ -28,7 +28,7 @@
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "pipelineCycler.h"
-
+#include "sparseArray.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : SliderTable
@@ -55,15 +55,17 @@ PUBLISHED:
 
   INLINE int get_num_sliders() const;
   INLINE const VertexSlider *get_slider(int n) const;
+  INLINE const SparseArray &get_slider_rows(int n) const;
 
-  INLINE const VertexSlider *find_slider(const InternalName *name) const;
+  INLINE const SparseArray &find_sliders(const InternalName *name) const;
   INLINE bool has_slider(const InternalName *name) const;
   INLINE bool is_empty() const;
   INLINE UpdateSeq get_modified(Thread *current_thread) const;
 
   void set_slider(int n, const VertexSlider *slider);
+  void set_slider_rows(int n, const SparseArray &rows);
   void remove_slider(int n);
-  int add_slider(const VertexSlider *slider);
+  int add_slider(const VertexSlider *slider, const SparseArray &rows);
 
   void write(ostream &out) const;
 
@@ -75,12 +77,20 @@ private:
 private:
   bool _is_registered;
 
-  typedef pvector< CPT(VertexSlider) > Sliders;
+  class SliderDef {
+  public:
+    CPT(VertexSlider) _slider;
+    SparseArray _rows;
+  };
+
+  typedef pvector<SliderDef> Sliders;
   Sliders _sliders;
 
-  typedef pmap< CPT(InternalName), const VertexSlider *> SlidersByName;
+  typedef pmap< CPT(InternalName), SparseArray > SlidersByName;
   SlidersByName _sliders_by_name;
 
+  static SparseArray _empty_array;
+
   // This is the data that must be cycled between pipeline stages.
   class EXPCL_PANDA CData : public CycleData {
   public:

+ 37 - 0
panda/src/gobj/transformBlendTable.I

@@ -88,6 +88,43 @@ get_max_simultaneous_transforms() const {
   return _max_simultaneous_transforms;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendTable::set_rows
+//       Access: Published
+//  Description: Specifies the subset of rows (vertices) in the
+//               associated GeomVertexData that this
+//               TransformBlendTable actually affects.
+////////////////////////////////////////////////////////////////////
+INLINE void TransformBlendTable::
+set_rows(const SparseArray &rows) {
+  _rows = rows;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendTable::get_rows
+//       Access: Published
+//  Description: Returns the subset of rows (vertices) in the
+//               associated GeomVertexData that this
+//               TransformBlendTable actually affects.
+////////////////////////////////////////////////////////////////////
+INLINE const SparseArray &TransformBlendTable::
+get_rows() const {
+  return _rows;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendTable::modify_rows
+//       Access: Published
+//  Description: Returns a modifiable reference to the SparseArray
+//               that specifies the subset of rows (vertices) in the
+//               associated GeomVertexData that this
+//               TransformBlendTable actually affects.
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray &TransformBlendTable::
+modify_rows() {
+  return _rows;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformBlendTable::consider_rebuild_index
 //       Access: Private

+ 13 - 1
panda/src/gobj/transformBlendTable.cxx

@@ -39,7 +39,8 @@ TransformBlendTable() {
 ////////////////////////////////////////////////////////////////////
 TransformBlendTable::
 TransformBlendTable(const TransformBlendTable &copy) :
-  _blends(copy._blends)
+  _blends(copy._blends),
+  _rows(copy._rows)
 {
 }
 
@@ -51,6 +52,7 @@ TransformBlendTable(const TransformBlendTable &copy) :
 void TransformBlendTable::
 operator = (const TransformBlendTable &copy) {
   _blends = copy._blends;
+  _rows = copy._rows;
   clear_index();
 }
 
@@ -229,6 +231,8 @@ write_datagram(BamWriter *manager, Datagram &dg) {
     (*bi).write_datagram(manager, dg);
   }
 
+  _rows.write_datagram(manager, dg);
+
   manager->write_cdata(dg, _cycler);
 }
 
@@ -291,6 +295,14 @@ fillin(DatagramIterator &scan, BamReader *manager) {
     _blends.push_back(blend);
   }
 
+  if (manager->get_file_minor_ver() >= 7) {
+    _rows.read_datagram(scan, manager);
+  } else {
+    // In this case, for bam files prior to 6.7, we must define the
+    // SparseArray with the full number of vertices.  This is done
+    // in GeomVertexData::complete_pointers().
+  }
+
   manager->read_cdata(scan, _cycler);
 }
 

+ 7 - 0
panda/src/gobj/transformBlendTable.h

@@ -32,6 +32,7 @@
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "pipelineCycler.h"
+#include "sparseArray.h"
 
 class FactoryParams;
 
@@ -68,6 +69,10 @@ PUBLISHED:
   INLINE int get_num_transforms() const;
   INLINE int get_max_simultaneous_transforms() const;
 
+  INLINE void set_rows(const SparseArray &rows);
+  INLINE const SparseArray &get_rows() const;
+  INLINE SparseArray &modify_rows();
+
   void write(ostream &out, int indent_level) const;
 
 private:
@@ -88,6 +93,8 @@ private:
   typedef pvector<TransformBlend> Blends;
   Blends _blends;
 
+  SparseArray _rows;
+
   // This map indexes directly into the above vector.  That means any
   // time we add or remove anything from the vector, we must
   // completely rebuild the index (since the vector might reallocate,

+ 9 - 1
panda/src/pgraph/geomTransformer.cxx

@@ -601,9 +601,15 @@ collect_vertex_data(Geom *geom, int collect_bits) {
       new_btable = new_data->modify_transform_blend_table();
     } else {
       new_btable = new TransformBlendTable;
+      new_btable->add_blend(TransformBlend());
       new_data->set_transform_blend_table(new_btable);
     }
 
+    SparseArray new_rows = old_btable->get_rows();
+    new_rows <<= offset;
+    new_rows |= new_btable->get_rows();
+    new_btable->set_rows(new_rows);
+
     // We still need to build up the IndexMap.
     IndexMap blend_map;
 
@@ -644,7 +650,9 @@ collect_vertex_data(Geom *geom, int collect_bits) {
     }
     int num_sliders = old_sliders->get_num_sliders();
     for (int si = 0; si < num_sliders; ++si) {
-      new_sliders->add_slider(old_sliders->get_slider(si));
+      SparseArray new_rows = old_sliders->get_slider_rows(si);
+      new_rows <<= offset;
+      new_sliders->add_slider(old_sliders->get_slider(si), new_rows);
     }
     new_data->set_slider_table(SliderTable::register_table(new_sliders));
   }

+ 1 - 1
panda/src/pgraph/nodePath.cxx

@@ -5779,7 +5779,7 @@ flatten_strong() {
   gr.apply_attribs(node());
   int num_removed = gr.flatten(node(), ~0);
 
-  gr.collect_vertex_data(node(), ~(SceneGraphReducer::CVD_format | SceneGraphReducer::CVD_name));
+  gr.collect_vertex_data(node(), ~(SceneGraphReducer::CVD_format | SceneGraphReducer::CVD_name | SceneGraphReducer::CVD_animation_type));
   gr.unify(node());
 
   return num_removed;

+ 1 - 4
panda/src/pgraph/sceneGraphReducer.h

@@ -100,10 +100,7 @@ PUBLISHED:
     CVD_usage_hint     = 0x040,
 
     // If set, GeomVertexDatas with unanimated vertices will not be
-    // combined with GeomVertexDatas with animated vertices.  Although
-    // it is legal to mix unanimated and animated vertex datas, doing
-    // so will convert the unanimated vertices to animated vertices,
-    // which can result in additional processing requirements.
+    // combined with GeomVertexDatas with animated vertices.
     CVD_animation_type = 0x080, 
   };
 

+ 2 - 1
panda/src/putil/bam.h

@@ -36,13 +36,14 @@ static const unsigned short _bam_major_ver = 6;
 // Bumped to major version 5 on 5/6/05 for new Geom implementation.
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 
-static const unsigned short _bam_minor_ver = 6;
+static const unsigned short _bam_minor_ver = 7;
 // Bumped to minor version 1 on 3/12/06 to add Texture::_compression.
 // Bumped to minor version 2 on 3/17/06 to add PandaNode::_draw_control_mask.
 // Bumped to minor version 3 on 3/21/06 to add Texture::_ram_images.
 // Bumped to minor version 4 on 7/26/06 to add CharacterJoint::_character.
 // Bumped to minor version 5 on 11/15/06 to add PartBundleNode::_num_bundles.
 // Bumped to minor version 6 on 2/5/07 to change GeomPrimitive::_num_vertices.
+// Bumped to minor version 7 on 2/15/07 to change SliderTable.
 
 
 #endif

+ 32 - 0
panda/src/putil/bitArray.cxx

@@ -808,3 +808,35 @@ normalize() {
     }
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: BitArray::write_datagram
+//       Access: Public
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void BitArray::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_uint32(_array.size());
+  Array::const_iterator ai;
+  for (ai = _array.begin(); ai != _array.end(); ++ai) {
+    dg.add_uint32((*ai).get_word());
+  }
+  dg.add_uint8(_highest_bits);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BitArray::read_datagram
+//       Access: Public
+//  Description: Reads the object that was previously written to a Bam
+//               file.
+////////////////////////////////////////////////////////////////////
+void BitArray::
+read_datagram(DatagramIterator &scan, BamReader *manager) {
+  size_t num_words = scan.get_uint32();
+  _array = Array::empty_array(num_words);
+  for (size_t i = 0; i < num_words; ++i) {
+    _array[i] = WordType(scan.get_uint32());
+  }
+  _highest_bits = scan.get_uint8();
+}

+ 8 - 0
panda/src/putil/bitArray.h

@@ -30,6 +30,10 @@
 #include "checksumHashGenerator.h"
 
 class SparseArray;
+class BamWriter;
+class BamReader;
+class Datagram;
+class DatagramIterator;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : BitArray
@@ -136,6 +140,10 @@ private:
   Array _array;
   int _highest_bits;  // Either 0 or 1.
 
+public:
+  void write_datagram(BamWriter *manager, Datagram &dg) const;
+  void read_datagram(DatagramIterator &scan, BamReader *manager);
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 35 - 0
panda/src/putil/sparseArray.cxx

@@ -515,3 +515,38 @@ do_shift(int offset) {
     }
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::write_datagram
+//       Access: Public
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_uint32(_subranges.size());
+  Subranges::const_iterator si;
+  for (si = _subranges.begin(); si != _subranges.end(); ++si) {
+    dg.add_int32((*si)._begin);
+    dg.add_int32((*si)._end);
+  }
+  dg.add_bool(_inverse);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::read_datagram
+//       Access: Public
+//  Description: Reads the object that was previously written to a Bam
+//               file.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+read_datagram(DatagramIterator &scan, BamReader *manager) {
+  size_t num_subranges = scan.get_uint32();
+  _subranges.reserve(num_subranges);
+  for (size_t i = 0; i < num_subranges; ++i) {
+    int begin = scan.get_int32();
+    int end = scan.get_int32();
+    _subranges.push_back(Subrange(begin, end));
+  }
+  _inverse = scan.get_bool();
+}

+ 8 - 0
panda/src/putil/sparseArray.h

@@ -23,6 +23,10 @@
 #include "ordered_vector.h"
 
 class BitArray;
+class BamWriter;
+class BamReader;
+class Datagram;
+class DatagramIterator;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : SparseArray
@@ -144,6 +148,10 @@ private:
   Subranges _subranges;
   bool _inverse;
 
+public:
+  void write_datagram(BamWriter *manager, Datagram &dg) const;
+  void read_datagram(DatagramIterator &scan, BamReader *manager);
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;