Forráskód Böngészése

beginning morph animations

David Rose 21 éve
szülő
commit
19ef896a9b

+ 3 - 2
panda/src/glstuff/glGeomMunger_src.cxx

@@ -50,9 +50,10 @@ munge_format_impl(const qpGeomVertexFormat *orig) {
   const qpGeomVertexDataType *color_type = 
     format->get_data_type(InternalName::get_color());
   if (color_type != (qpGeomVertexDataType *)NULL &&
-      color_type->get_numeric_type() == qpGeomVertexDataType::NT_packed_8888) {
+      (color_type->get_numeric_type() == qpGeomVertexDataType::NT_packed_8888 ||
+       color_type->get_contents() != qpGeomVertexDataType::NT_rgba)) {
     // We need to convert the color format; OpenGL doesn't support the
-    // byte order of DirectX's packed_argb format.
+    // byte order of DirectX's packed ARGB format.
     int color_array = format->get_array_with(InternalName::get_color());
 
     PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(*format);

+ 1 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2676,7 +2676,7 @@ prepare_index_buffer(qpGeomPrimitive *data) {
 
     if (GLCAT.is_debug()) {
       GLCAT.debug()
-        << "creating vertex buffer " << gibc->_index << "\n";
+        << "creating index buffer " << gibc->_index << "\n";
     }
     
     report_my_gl_errors();

+ 11 - 1
panda/src/gobj/Sources.pp

@@ -47,6 +47,7 @@
     preparedGraphicsObjects.I preparedGraphicsObjects.h \
     lens.h lens.I \
     savedContext.I savedContext.h \
+    sliderTable.I sliderTable.h \
     texture.I texture.h \
     textureContext.I textureContext.h \
     texturePool.I texturePool.h \
@@ -54,8 +55,10 @@
     transformBlend.I transformBlend.h \
     transformBlendPalette.I transformBlendPalette.h \
     transformPalette.I transformPalette.h \
+    userVertexSlider.I userVertexSlider.h \
     userVertexTransform.I userVertexTransform.h \
     vertexBufferContext.I vertexBufferContext.h \
+    vertexSlider.I vertexSlider.h \
     vertexTransform.I vertexTransform.h
     
   #define INCLUDED_SOURCES \
@@ -92,13 +95,17 @@
     perspectiveLens.cxx \
     preparedGraphicsObjects.cxx \
     lens.cxx  \
-    savedContext.cxx texture.cxx textureContext.cxx texturePool.cxx \
+    savedContext.cxx \
+    sliderTable.cxx \
+    texture.cxx textureContext.cxx texturePool.cxx \
     textureStage.cxx \
     transformBlend.cxx \
     transformBlendPalette.cxx \
     transformPalette.cxx \
+    userVertexSlider.cxx \
     userVertexTransform.cxx \
     vertexBufferContext.cxx \
+    vertexSlider.cxx \
     vertexTransform.cxx
 
   #define INSTALL_HEADERS \
@@ -139,6 +146,7 @@
     preparedGraphicsObjects.I preparedGraphicsObjects.h \
     lens.h lens.I \
     savedContext.I savedContext.h \
+    sliderTable.I sliderTable.h \
     texture.I texture.h \
     textureContext.I textureContext.h \
     texturePool.I texturePool.h \
@@ -146,8 +154,10 @@
     transformBlend.I transformBlend.h \
     transformBlendPalette.I transformBlendPalette.h \
     transformPalette.I transformPalette.h \
+    userVertexSlider.I userVertexSlider.h \
     userVertexTransform.I userVertexTransform.h \
     vertexBufferContext.I vertexBufferContext.h \
+    vertexSlider.I vertexSlider.h \
     vertexTransform.I vertexTransform.h
 
 

+ 8 - 0
panda/src/gobj/config_gobj.cxx

@@ -40,13 +40,16 @@
 #include "matrixLens.h"
 #include "perspectiveLens.h"
 #include "lens.h"
+#include "sliderTable.h"
 #include "texture.h"
 #include "textureStage.h"
 #include "textureContext.h"
 #include "transformBlendPalette.h"
 #include "transformPalette.h"
+#include "userVertexSlider.h"
 #include "userVertexTransform.h"
 #include "vertexTransform.h"
+#include "vertexSlider.h"
 #include "geomContext.h"
 #include "vertexBufferContext.h"
 #include "indexBufferContext.h"
@@ -212,13 +215,16 @@ ConfigureFn(config_gobj) {
   MatrixLens::init_type();
   PerspectiveLens::init_type();
   Lens::init_type();
+  SliderTable::init_type();
   Texture::init_type();
   dDrawable::init_type();
   TextureStage::init_type();
   TransformBlendPalette::init_type();
   TransformPalette::init_type();
+  UserVertexSlider::init_type();
   UserVertexTransform::init_type();
   VertexTransform::init_type();
+  VertexSlider::init_type();
   InternalName::init_type();
 
   //Registration of writeable object's creation
@@ -248,10 +254,12 @@ ConfigureFn(config_gobj) {
   OrthographicLens::register_with_read_factory();
   MatrixLens::register_with_read_factory();
   PerspectiveLens::register_with_read_factory();
+  SliderTable::register_with_read_factory();
   Texture::register_with_read_factory();
   TextureStage::register_with_read_factory();
   TransformBlendPalette::register_with_read_factory();
   TransformPalette::register_with_read_factory();
+  UserVertexSlider::register_with_read_factory();
   UserVertexTransform::register_with_read_factory();
   InternalName::register_with_read_factory();
 }

+ 3 - 0
panda/src/gobj/gobj_composite2.cxx

@@ -12,6 +12,7 @@
 #include "perspectiveLens.cxx"
 #include "preparedGraphicsObjects.cxx"
 #include "savedContext.cxx"
+#include "sliderTable.cxx"
 #include "texture.cxx"
 #include "textureContext.cxx"
 #include "texturePool.cxx"
@@ -19,6 +20,8 @@
 #include "transformBlend.cxx"
 #include "transformBlendPalette.cxx"
 #include "transformPalette.cxx"
+#include "userVertexSlider.cxx"
 #include "userVertexTransform.cxx"
 #include "vertexBufferContext.cxx"
+#include "vertexSlider.cxx"
 #include "vertexTransform.cxx"

+ 68 - 0
panda/src/gobj/internalName.cxx

@@ -111,6 +111,74 @@ get_name() const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::find_ancestor
+//       Access: Published
+//  Description: Returns the index of the ancestor with the indicated
+//               basename, or -1 if no ancestor has that basename.
+//               Returns 0 if this name has the basename.
+//
+//               This index value may be passed to get_ancestor() or
+//               get_net_basename() to retrieve more information about
+//               the indicated name.
+////////////////////////////////////////////////////////////////////
+int InternalName::
+find_ancestor(const string &basename) const {
+  if (_basename == basename) {
+    return 0;
+
+  } else if (_parent != (InternalName *)NULL) {
+    int index = _parent->find_ancestor(basename);
+    if (index >= 0) {
+      return index + 1;
+    }
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::get_ancestor
+//       Access: Published
+//  Description: Returns the ancestor with the indicated index number.
+//               0 is this name itself, 1 is the name's parent, 2 is
+//               the parent's parent, and so on.  If there are not
+//               enough ancestors, returns the root InternalName.
+////////////////////////////////////////////////////////////////////
+const InternalName *InternalName::
+get_ancestor(int n) const {
+  if (n == 0) {
+    return this;
+
+  } else if (_parent != (InternalName *)NULL) {
+    return _parent->get_ancestor(n - 1);
+
+  } else {
+    return get_root();
+  } 
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::get_net_basename
+//       Access: Published
+//  Description: Returns the basename of this name prefixed by the
+//               indicated number of ancestors.  0 is this name's
+//               basename, 1 is parent.basename, 2 is
+//               grandparent.parent.basename, and so on.
+////////////////////////////////////////////////////////////////////
+string InternalName::
+get_net_basename(int n) const {
+  if (n == 0) {
+    return _basename;
+
+  } else if (_parent != (InternalName *)NULL && _parent != get_root()) {
+    return _parent->get_net_basename(n - 1) + "." + _basename;
+
+  } else {
+    return _basename;
+  } 
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: InternalName::output
 //       Access: Published

+ 4 - 0
panda/src/gobj/internalName.h

@@ -55,6 +55,10 @@ PUBLISHED:
   string get_name() const;
   INLINE const string &get_basename() const;
 
+  int find_ancestor(const string &basename) const;
+  const InternalName *get_ancestor(int n) const;
+  string get_net_basename(int n) const;
+
   void output(ostream &out) const;
 
   // Some predefined built-in names.

+ 29 - 0
panda/src/gobj/qpgeomVertexData.I

@@ -150,6 +150,35 @@ clear_transform_blend_palette() {
   set_transform_blend_palette(NULL);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_slider_table
+//       Access: Published
+//  Description: Returns a const pointer to the SliderTable
+//               assigned to this data.  Vertices within the vertex
+//               data will look up their morph offsets, if any, within
+//               this table.
+//
+//               This will return NULL if the vertex data does not
+//               have a SliderTable assigned.
+////////////////////////////////////////////////////////////////////
+INLINE const SliderTable *qpGeomVertexData::
+get_slider_table() const {
+  CDReader cdata(_cycler);
+  return cdata->_slider_table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::clear_slider_table
+//       Access: Published
+//  Description: Sets the SliderTable pointer to NULL,
+//               removing the table from the vertex data.  This
+//               disables morph (blend shape) animation.
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomVertexData::
+clear_slider_table() {
+  set_slider_table(NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::get_modified
 //       Access: Published

+ 304 - 145
panda/src/gobj/qpgeomVertexData.cxx

@@ -19,6 +19,7 @@
 #include "qpgeomVertexData.h"
 #include "qpgeomVertexReader.h"
 #include "qpgeomVertexWriter.h"
+#include "qpgeomVertexRewriter.h"
 #include "pStatTimer.h"
 #include "bamReader.h"
 #include "bamWriter.h"
@@ -241,6 +242,51 @@ set_transform_blend_palette(const TransformBlendPalette *palette) {
   cdata->_animated_vertices_modified = UpdateSeq();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::modify_slider_table
+//       Access: Published
+//  Description: Returns a modifiable pointer to the current
+//               SliderTable on this vertex data, if any, or
+//               NULL if there is not a SliderTable.  See
+//               get_slider_table().
+////////////////////////////////////////////////////////////////////
+SliderTable *qpGeomVertexData::
+modify_slider_table() {
+  // Perform copy-on-write: if the reference count on the table is
+  // greater than 1, assume some other GeomVertexData has the same
+  // pointer, so make a copy of it first.
+  CDWriter cdata(_cycler);
+
+  if (cdata->_slider_table->get_ref_count() > 1) {
+    cdata->_slider_table = new SliderTable(*cdata->_slider_table);
+  }
+  cdata->_modified = qpGeom::get_next_modified();
+  cdata->_animated_vertices_modified = UpdateSeq();
+
+  return cdata->_slider_table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::set_slider_table
+//       Access: Published
+//  Description: Replaces the SliderTable on this vertex
+//               data with the indicated table.  There should be an
+//               entry in this table for each kind of morph offset
+//               defined in the vertex data.
+//
+//               The SliderTable object must have been registered
+//               prior to setting it on the GeomVertexData.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+set_slider_table(const SliderTable *table) {
+  nassertv(table == (SliderTable *)NULL || table->is_registered());
+
+  CDWriter cdata(_cycler);
+  cdata->_slider_table = (SliderTable *)table;
+  cdata->_modified = qpGeom::get_next_modified();
+  cdata->_animated_vertices_modified = UpdateSeq();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::get_num_bytes
 //       Access: Published
@@ -262,127 +308,132 @@ get_num_bytes() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::convert_to
+//     Function: qpGeomVertexData::copy_from
 //       Access: Published
-//  Description: Matches up the data types of this format with the
-//               data types of the other format by name, and copies
-//               the data vertex-by-vertex to a new set of data arrays
-//               in the new format.
+//  Description: Copies all the data from the other array into the
+//               corresponding data types in this array, by matching
+//               data types name-by-name.
+//
+//               keep_data_objects specifies what to do when one or
+//               more of the arrays can be copied without the need to
+//               apply any conversion operation.  If it is true, the
+//               original GeomVertexArrayData objects in this object
+//               are retained, but their data arrays are copied
+//               pointerwise from the source; if it is false, then the
+//               GeomVertexArrayData objects themselves are copied
+//               pointerwise from the source.
 ////////////////////////////////////////////////////////////////////
-CPT(qpGeomVertexData) qpGeomVertexData::
-convert_to(const qpGeomVertexFormat *new_format) const {
-  if (new_format == _format) {
-    // Trivial case: no change is needed.
-    return this;
-  }
-
-  // Okay, convert the data to the new format.
-  int num_vertices = get_num_vertices();
-
-  if (gobj_cat.is_debug()) {
-    gobj_cat.debug()
-      << "Converting " << num_vertices << " vertices.\n";
-  }
-  PStatTimer timer(_convert_pcollector);
-
-  PT(qpGeomVertexData) new_data = 
-    new qpGeomVertexData(get_name(), new_format, get_usage_hint());
-  new_data->set_transform_blend_palette(get_transform_blend_palette());
-
-  pset<int> done_arrays;
+void qpGeomVertexData::
+copy_from(const qpGeomVertexData &source, bool keep_data_objects) {
+  const qpGeomVertexFormat *source_format = source.get_format();
+  const qpGeomVertexFormat *dest_format = get_format();
 
-  int num_arrays = _format->get_num_arrays();
-  int i;
+  int num_vertices = source.get_num_vertices();
+  int num_arrays = source_format->get_num_arrays();
+  int source_i;
 
   // First, check to see if any arrays can be simply appropriated for
   // the new format, without changing the data.
-  for (i = 0; i < num_arrays; ++i) {
-    const qpGeomVertexArrayFormat *array_format = 
-      _format->get_array(i);
+  pset<int> done_arrays;
+
+  for (source_i = 0; source_i < num_arrays; ++source_i) {
+    const qpGeomVertexArrayFormat *source_array_format = 
+      source_format->get_array(source_i);
 
     bool array_done = false;
 
-    int new_num_arrays = new_format->get_num_arrays();
-    for (int new_i = 0; 
-         new_i < new_num_arrays && !array_done; 
-         ++new_i) {
-      const qpGeomVertexArrayFormat *new_array_format = 
-        new_format->get_array(new_i);
-      if (new_array_format->is_data_subset_of(*array_format)) {
+    int dest_num_arrays = dest_format->get_num_arrays();
+    for (int dest_i = 0; 
+         dest_i < dest_num_arrays && !array_done; 
+         ++dest_i) {
+      const qpGeomVertexArrayFormat *dest_array_format = 
+        dest_format->get_array(dest_i);
+      if (dest_array_format->is_data_subset_of(*source_array_format)) {
         // Great!  Just use the same data for this one.
-        new_data->set_array(new_i, get_array(i));
-        array_done = true;
+        if (keep_data_objects) {
+          // Copy the data, but keep the same GeomVertexArrayData object.  
+
+          // Maybe it even has the same data pointer already.  If so,
+          // avoid flipping the modified flag.
+          CPTA_uchar source_data = source.get_array(source_i)->get_data();
+          if (get_array(dest_i)->get_data() != source_data) {
+            modify_array(dest_i)->set_data(source_data);
+          }
+        } else {
+          // Copy the GeomVertexArrayData object.
+          if (get_array(dest_i) != source.get_array(source_i)) {
+            set_array(dest_i, source.get_array(source_i));
+          }
+        }
 
-        done_arrays.insert(new_i);
+        array_done = true;
+        done_arrays.insert(dest_i);
       }
     }
   }
 
   // Now make sure the arrays we didn't share are all filled in.
-  new_data->set_num_vertices(num_vertices);
+  set_num_vertices(num_vertices);
 
   // Now go back through and copy any data that's left over.
-  for (i = 0; i < num_arrays; ++i) {
-    CPTA_uchar array_data = get_array(i)->get_data();
-    const qpGeomVertexArrayFormat *array_format = _format->get_array(i);
-    int num_data_types = array_format->get_num_data_types();
+  for (source_i = 0; source_i < num_arrays; ++source_i) {
+    CPTA_uchar array_data = source.get_array(source_i)->get_data();
+    const qpGeomVertexArrayFormat *source_array_format = source_format->get_array(source_i);
+    int num_data_types = source_array_format->get_num_data_types();
     for (int di = 0; di < num_data_types; ++di) {
-      const qpGeomVertexDataType *data_type = array_format->get_data_type(di);
+      const qpGeomVertexDataType *source_data_type = source_array_format->get_data_type(di);
 
-      int new_i = new_format->get_array_with(data_type->get_name());
-      if (new_i >= 0 && done_arrays.count(new_i) == 0) {
+      int dest_i = dest_format->get_array_with(source_data_type->get_name());
+      if (dest_i >= 0 && done_arrays.count(dest_i) == 0) {
         // The data type exists in the new format; we have to copy it.
-        const qpGeomVertexArrayFormat *new_array_format = 
-          new_format->get_array(new_i);
-        const qpGeomVertexDataType *new_data_type = 
-          new_array_format->get_data_type(data_type->get_name());
+        const qpGeomVertexArrayFormat *dest_array_format = 
+          dest_format->get_array(dest_i);
+        const qpGeomVertexDataType *dest_data_type = 
+          dest_array_format->get_data_type(source_data_type->get_name());
 
-        if (new_data_type->is_bytewise_equivalent(*data_type)) {
+        if (dest_data_type->is_bytewise_equivalent(*source_data_type)) {
           // We can do a quick bytewise copy.
-          PTA_uchar new_array_data = 
-            new_data->modify_array(new_i)->modify_data();
+          PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
 
-          bytewise_copy(new_array_data + new_data_type->get_start(), 
-                        new_array_format->get_stride(),
-                        array_data + data_type->get_start(), array_format->get_stride(),
-                        data_type, num_vertices);
+          bytewise_copy(dest_array_data + dest_data_type->get_start(), 
+                        dest_array_format->get_stride(),
+                        array_data + source_data_type->get_start(), source_array_format->get_stride(),
+                        source_data_type, num_vertices);
 
-        } else if (new_data_type->is_packed_argb() && 
-                   data_type->is_uint8_rgba()) {
+        } else if (dest_data_type->is_packed_argb() && 
+                   source_data_type->is_uint8_rgba()) {
           // A common special case: OpenGL color to DirectX color.
-          PTA_uchar new_array_data = 
-            new_data->modify_array(new_i)->modify_data();
+          PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
 
           uint8_rgba_to_packed_argb
-            (new_array_data + new_data_type->get_start(), 
-             new_array_format->get_stride(),
-             array_data + data_type->get_start(), array_format->get_stride(),
+            (dest_array_data + dest_data_type->get_start(), 
+             dest_array_format->get_stride(),
+             array_data + source_data_type->get_start(), source_array_format->get_stride(),
              num_vertices);
 
-        } else if (new_data_type->is_uint8_rgba() && 
-                   data_type->is_packed_argb()) {
+        } else if (dest_data_type->is_uint8_rgba() && 
+                   source_data_type->is_packed_argb()) {
           // Another common special case: DirectX color to OpenGL
           // color.
-          PTA_uchar new_array_data = 
-            new_data->modify_array(new_i)->modify_data();
+          PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
 
           packed_argb_to_uint8_rgba
-            (new_array_data + new_data_type->get_start(), 
-             new_array_format->get_stride(),
-             array_data + data_type->get_start(), array_format->get_stride(),
+            (dest_array_data + dest_data_type->get_start(), 
+             dest_array_format->get_stride(),
+             array_data + source_data_type->get_start(), source_array_format->get_stride(),
              num_vertices);
 
         } else {
           // A generic copy.
           if (gobj_cat.is_debug()) {
             gobj_cat.debug()
-              << "generic copy " << *new_data_type << " from " 
-              << *data_type << "\n";
+              << "generic copy " << *dest_data_type << " from " 
+              << *source_data_type << "\n";
           }
-          qpGeomVertexWriter to(new_data);
-          to.set_data_type(new_i, new_data_type);
-          qpGeomVertexReader from(this);
-          from.set_data_type(i, data_type);
+          qpGeomVertexWriter to(this);
+          to.set_data_type(dest_i, dest_data_type);
+          qpGeomVertexReader from(&source);
+          from.set_data_type(source_i, source_data_type);
 
           while (!from.is_at_end()) {
             to.set_data4f(from.get_data4f());
@@ -391,7 +442,35 @@ convert_to(const qpGeomVertexFormat *new_format) const {
       }
     }
   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::convert_to
+//       Access: Published
+//  Description: Returns a new GeomVertexData that represents the same
+//               contents as this one, with all data types matched up
+//               name-by-name to the indicated new format.
+////////////////////////////////////////////////////////////////////
+CPT(qpGeomVertexData) qpGeomVertexData::
+convert_to(const qpGeomVertexFormat *new_format) const {
+  if (new_format == _format) {
+    // Trivial case: no change is needed.
+    return this;
+  }
 
+  // Okay, convert the data to the new format.
+  if (gobj_cat.is_debug()) {
+    gobj_cat.debug()
+      << "Converting " << get_num_vertices() << " vertices.\n";
+  }
+  PStatTimer timer(_convert_pcollector);
+
+  PT(qpGeomVertexData) new_data = 
+    new qpGeomVertexData(get_name(), new_format, get_usage_hint());
+  new_data->set_transform_blend_palette(get_transform_blend_palette());
+  new_data->set_slider_table(get_slider_table());
+
+  new_data->copy_from(*this, false);
   return new_data;
 }
 
@@ -542,6 +621,7 @@ replace_data_type(const InternalName *name, int num_components,
     new qpGeomVertexData(get_name(), format, usage_hint);
   if (keep_animation) {
     new_data->set_transform_blend_palette(get_transform_blend_palette());
+    new_data->set_slider_table(get_slider_table());
   }
 
   int j = 0;
@@ -644,27 +724,35 @@ get_array_info(const InternalName *name,
 CPT(qpGeomVertexData) qpGeomVertexData::
 do_animate_vertices(bool from_app) const {
   CDReader cdata(_cycler);
-  if (cdata->_transform_blend_palette == (TransformBlendPalette *)NULL) {
-    // No vertex animation.
+
+  UpdateSeq modified;
+  if (cdata->_transform_blend_palette != (TransformBlendPalette *)NULL) {
+    if (cdata->_slider_table != (SliderTable *)NULL) {
+      modified = 
+        max(cdata->_transform_blend_palette->get_modified(),
+            cdata->_slider_table->get_modified());
+    } else {
+      modified = cdata->_transform_blend_palette->get_modified();
+    }
+
+  } else if (cdata->_slider_table != (SliderTable *)NULL) {
+    modified = cdata->_slider_table->get_modified();
+
+  } else {
+    // No transform blend palette or slider table--ergo, no vertex
+    // animation.
     return this;
   }
 
-  if (cdata->_animated_vertices == (qpGeomVertexData *)NULL) {
-    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
-    ((qpGeomVertexData *)this)->make_animated_vertices(cdataw);
-    ((qpGeomVertexData *)this)->update_animated_vertices(cdataw, from_app);
-    return cdataw->_animated_vertices;
-  } else {
-    UpdateSeq blend_modified = cdata->_transform_blend_palette->get_modified();
-    if (cdata->_animated_vertices_modified == blend_modified) {
-      // No changes.
-      return cdata->_animated_vertices;
-    }
-    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
-    cdataw->_animated_vertices_modified = blend_modified;
-    ((qpGeomVertexData *)this)->update_animated_vertices(cdataw, from_app);
-    return cdataw->_animated_vertices;
+  if (cdata->_animated_vertices_modified == modified &&
+      cdata->_animated_vertices != (qpGeomVertexData *)NULL) {
+    // No changes.
+    return cdata->_animated_vertices;
   }
+  CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
+  cdataw->_animated_vertices_modified = modified;
+  ((qpGeomVertexData *)this)->update_animated_vertices(cdataw, from_app);
+  return cdataw->_animated_vertices;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -787,22 +875,6 @@ do_set_num_vertices(int n, qpGeomVertexData::CDWriter &cdata) {
   return any_changed;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::make_animated_vertices
-//       Access: Private
-//  Description: Creates the GeomVertexData that represents the
-//               results of computing the vertex animation on the CPU.
-////////////////////////////////////////////////////////////////////
-void qpGeomVertexData::
-make_animated_vertices(qpGeomVertexData::CDWriter &cdata) {
-  // First, make a new format that doesn't have the transform_blend
-  // array.
-  cdata->_animated_vertices = replace_data_type
-    (InternalName::get_transform_blend(), 0, qpGeomVertexDataType::NT_uint16,
-     qpGeomVertexDataType::C_index,
-     min(get_usage_hint(), qpGeomUsageHint::UH_dynamic), false);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::update_animated_vertices
 //       Access: Private
@@ -825,48 +897,135 @@ update_animated_vertices(qpGeomVertexData::CDWriter &cdata, bool from_app) {
   PStatTimer timer(collector);
 #endif
 
-  CPT(TransformBlendPalette) palette = cdata->_transform_blend_palette;
-  nassertv(palette != (TransformBlendPalette *)NULL);
-
-  // Recompute all the blends up front, so we don't have to test each
-  // one for staleness at each vertex.
-  int num_blends = palette->get_num_blends();
-  int bi;
-  for (bi = 0; bi < num_blends; bi++) {
-    palette->get_blend(bi).update_blend();
+  if (cdata->_animated_vertices == (qpGeomVertexData *)NULL) {
+    CPT(qpGeomVertexFormat) animated_format = get_post_animated_format();
+    cdata->_animated_vertices = 
+      new qpGeomVertexData(get_name(), animated_format,
+                           min(get_usage_hint(), qpGeomUsageHint::UH_dynamic));
   }
-
   PT(qpGeomVertexData) new_data = cdata->_animated_vertices;
 
-  // Now go through and compute the animation.
-  qpGeomVertexWriter to(new_data, InternalName::get_vertex());
-  qpGeomVertexReader from(this, InternalName::get_vertex());
-  qpGeomVertexReader blendi(this, InternalName::get_transform_blend());
-
-  if (!blendi.has_data_type()) {
-    gobj_cat.warning()
-      << "Vertex data " << get_name()
-      << " has a transform_blend_palette, but no transform_blend data.\n";
-    return;
+  // We have to make a complete copy of data first so we can modify
+  // it.  If we were clever, we could maybe just figure out the subset
+  // of the data that might have changed since last frame, but that's
+  // too much trouble (and isn't obviously faster than just copying
+  // the whole thing).
+  new_data->copy_from(*this, true);
+
+  // First, apply all of the morphs.
+  CPT(SliderTable) table = cdata->_slider_table;
+  if (table != (SliderTable *)NULL) {
+    int num_morphs = _format->get_num_morphs();
+    for (int mi = 0; mi < num_morphs; mi++) {
+      CPT(InternalName) slider_name = _format->get_morph_slider(mi);
+      const VertexSlider *slider = table->get_slider(slider_name);
+      if (slider != (VertexSlider *)NULL) {
+        float slider_value = slider->get_slider();
+        if (slider_value != 0.0f) {
+          CPT(InternalName) base_name = _format->get_morph_base(mi);
+          CPT(InternalName) delta_name = _format->get_morph_delta(mi);
+          
+          qpGeomVertexRewriter data(new_data, base_name);
+          qpGeomVertexReader delta(this, delta_name);
+
+          if (data.get_data_type()->get_num_values() == 4) {
+            if (data.get_data_type()->has_homogeneous_coord()) {
+              for (int i = 0; i < num_vertices; 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_vertices; i++) {
+                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_vertices; i++) {
+              const LPoint3f &vertex = data.get_data3f();
+              LPoint3f d = delta.get_data3f();
+              data.set_data3f(vertex + d * slider_value);
+            }
+          }
+        }
+      }
+    }
   }
 
-  if (from.get_data_type()->get_num_values() == 4) {
-    for (int i = 0; i < num_vertices; i++) {
-      LPoint4f vertex = from.get_data4f();
-      int bi = blendi.get_data1i();
-      palette->get_blend(bi).transform_point(vertex);
-      to.set_data4f(vertex);
+  // Then apply the transforms.
+  CPT(TransformBlendPalette) palette = cdata->_transform_blend_palette;
+  if (palette != (TransformBlendPalette *)NULL) {
+
+    // Recompute all the blends up front, so we don't have to test
+    // each one for staleness at each vertex.
+    int num_blends = palette->get_num_blends();
+    int bi;
+    for (bi = 0; bi < num_blends; bi++) {
+      palette->get_blend(bi).update_blend();
     }
-  } else {
-    for (int i = 0; i < num_vertices; i++) {
-      LPoint3f vertex = from.get_data3f();
-      int bi = blendi.get_data1i();
-      palette->get_blend(bi).transform_point(vertex);
-      to.set_data3f(vertex);
+
+    // Now go through and apply the transforms.
+    qpGeomVertexRewriter data(new_data, InternalName::get_vertex());
+    qpGeomVertexReader blendi(this, InternalName::get_transform_blend());
+
+    if (!blendi.has_data_type()) {
+      gobj_cat.warning()
+        << "Vertex data " << get_name()
+        << " has a transform_blend_palette, but no transform_blend data.\n";
+      return;
+    }
+    
+    if (data.get_data_type()->get_num_values() == 4) {
+      for (int i = 0; i < num_vertices; i++) {
+        LPoint4f vertex = data.get_data4f();
+        int bi = blendi.get_data1i();
+        palette->get_blend(bi).transform_point(vertex);
+        data.set_data4f(vertex);
+      }
+    } else {
+      for (int i = 0; i < num_vertices; i++) {
+        LPoint3f vertex = data.get_data3f();
+        int bi = blendi.get_data1i();
+        palette->get_blend(bi).transform_point(vertex);
+        data.set_data3f(vertex);
+      }
     }
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_post_animated_format
+//       Access: Private
+//  Description: Returns a suitable vertex format for sending the
+//               animated vertices to the graphics backend.  This is
+//               the same format as the source format, with the
+//               CPU-animation data elements removed.
+////////////////////////////////////////////////////////////////////
+CPT(qpGeomVertexFormat) qpGeomVertexData::
+get_post_animated_format() const {
+  PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(*_format);
+
+  new_format->remove_data_type(InternalName::get_transform_blend());
+
+  int num_morphs = _format->get_num_morphs();
+  for (int mi = 0; mi < num_morphs; mi++) {
+    CPT(InternalName) delta_name = _format->get_morph_delta(mi);
+    new_format->remove_data_type(delta_name);
+  }
+
+  return qpGeomVertexFormat::register_format(new_format);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::register_with_read_factory
 //       Access: Public, Static

+ 9 - 1
panda/src/gobj/qpgeomVertexData.h

@@ -26,6 +26,7 @@
 #include "qpgeomVertexArrayData.h"
 #include "qpgeomUsageHint.h"
 #include "transformBlendPalette.h"
+#include "sliderTable.h"
 #include "internalName.h"
 #include "cycleData.h"
 #include "cycleDataReader.h"
@@ -91,9 +92,15 @@ PUBLISHED:
   void set_transform_blend_palette(const TransformBlendPalette *palette);
   INLINE void clear_transform_blend_palette();
 
+  INLINE const SliderTable *get_slider_table() const;
+  SliderTable *modify_slider_table();
+  void set_slider_table(const SliderTable *palette);
+  INLINE void clear_slider_table();
+
   int get_num_bytes() const;
   INLINE UpdateSeq get_modified() const;
 
+  void copy_from(const qpGeomVertexData &source, bool keep_data_objects);
   CPT(qpGeomVertexData) convert_to(const qpGeomVertexFormat *new_format) const;
   CPT(qpGeomVertexData) 
     scale_color(const LVecBase4f &color_scale, int num_components,
@@ -167,6 +174,7 @@ private:
 
     Arrays _arrays;
     PT(TransformBlendPalette) _transform_blend_palette;
+    PT(SliderTable) _slider_table;
     PT(qpGeomVertexData) _animated_vertices;
     UpdateSeq _animated_vertices_modified;
     UpdateSeq _modified;
@@ -178,8 +186,8 @@ private:
 
 private:
   bool do_set_num_vertices(int n, CDWriter &cdata);
-  void make_animated_vertices(CDWriter &cdata);
   void update_animated_vertices(CDWriter &cdata, bool from_app);
+  CPT(qpGeomVertexFormat) get_post_animated_format() const;
 
   static PStatCollector _convert_pcollector;
   static PStatCollector _scale_color_pcollector;

+ 24 - 0
panda/src/gobj/qpgeomVertexDataType.I

@@ -151,6 +151,30 @@ get_total_bytes() const {
   return _total_bytes;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexDataType::has_homogeneous_coord
+//       Access: Published
+//  Description: Returns true if this Contents type is one that
+//               includes a homogeneous coordinate in the fourth
+//               component, or false otherwise.  If this is true,
+//               correct operation on the vertex data may require
+//               scaling by the homogeneous coordinate from time to
+//               time (but in general this is handled automatically if
+//               you use the 3-component or smaller forms of
+//               get_data() and set_data()).
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexDataType::
+has_homogeneous_coord() const {
+  switch (_contents) {
+  case C_point:
+  case C_texcoord:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexDataType::overlaps_with
 //       Access: Published

+ 2 - 0
panda/src/gobj/qpgeomVertexDataType.h

@@ -49,6 +49,7 @@ PUBLISHED:
     C_rgba,         // RGB or RGBA, OpenGL-style order
     C_argb,         // RGBA, DirectX-style packed color
     C_index,        // An index value into some other table
+    C_morph_delta,  // A delta from some base value, defining a blend shape
   };
 
   INLINE qpGeomVertexDataType(const InternalName *name, int num_components,
@@ -67,6 +68,7 @@ PUBLISHED:
   INLINE int get_start() const;
   INLINE int get_component_bytes() const;
   INLINE int get_total_bytes() const;
+  INLINE bool has_homogeneous_coord() const;
 
   INLINE bool overlaps_with(int start_byte, int num_bytes) const;
   INLINE bool is_bytewise_equivalent(const qpGeomVertexDataType &other) const;

+ 75 - 0
panda/src/gobj/qpgeomVertexFormat.I

@@ -93,6 +93,81 @@ get_array(int array) const {
   return _arrays[array];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_num_morphs
+//       Access: Published
+//  Description: Returns the number of data types within the format
+//               that represent morph deltas.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexFormat::
+get_num_morphs() const {
+  nassertr(_is_registered, 0);
+  
+  return _morphs.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_morph_slider
+//       Access: Published
+//  Description: Returns the slider name associated with the nth morph
+//               data type.  This is the name of the slider that will
+//               control the morph, and should be defined within the
+//               SliderTable associated with the GeomVertexData.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE const InternalName *qpGeomVertexFormat::
+get_morph_slider(int n) const {
+  nassertr(_is_registered, NULL);
+  nassertr(n >= 0 && n < (int)_morphs.size(), NULL);
+
+  return _morphs[n]._slider;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_morph_base
+//       Access: Published
+//  Description: Returns the name of the base data type that the nth
+//               morph modifies.  This data type will also be defined
+//               within the format, and can be retrieved via
+//               get_array_with() and/or get_data_type().
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE const InternalName *qpGeomVertexFormat::
+get_morph_base(int n) const {
+  nassertr(_is_registered, NULL);
+  nassertr(n >= 0 && n < (int)_morphs.size(), NULL);
+
+  return _morphs[n]._base;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_morph_delta
+//       Access: Published
+//  Description: Returns the name of the data type that defines the
+//               nth morph.  This contains the delta offsets that are
+//               to be applied to the data type defined by
+//               get_morph_base().  This data type will be defined
+//               within the format, and can be retrieved via
+//               get_array_with() and/or get_data_type().
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE const InternalName *qpGeomVertexFormat::
+get_morph_delta(int n) const {
+  nassertr(_is_registered, NULL);
+  nassertr(n >= 0 && n < (int)_morphs.size(), NULL);
+
+  return _morphs[n]._delta;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexFormat::get_v3
 //       Access: Published, Static

+ 36 - 0
panda/src/gobj/qpgeomVertexFormat.cxx

@@ -506,6 +506,42 @@ do_register() {
     }
   }
 
+  // Go back through the index now and look for morph descriptions.
+  DataTypesByName::iterator ni;
+  for (ni = _data_types_by_name.begin(); 
+       ni != _data_types_by_name.end();
+       ++ni) {
+    const DataTypeRecord &record = (*ni).second;
+    const qpGeomVertexDataType *data_type = _arrays[record._array_index]->get_data_type(record._data_type_index);
+
+    // Is it a morph description?
+    if (data_type->get_contents() == qpGeomVertexDataType::C_morph_delta) {
+      MorphRecord morph;
+      morph._delta = data_type->get_name();
+      
+      // The delta name must be of the form "basename.morph.slidername".  
+      int n = morph._delta->find_ancestor("morph");
+      if (n < 0) {
+        gobj_cat.warning()
+          << "vertex format defines " << *data_type->get_name()
+          << ", which is stored as a C_morph_delta, but its name does not include \"morph\".\n";
+      } else {
+        morph._slider = InternalName::make(morph._delta->get_net_basename(n - 1));
+        morph._base = morph._delta->get_ancestor(n + 1);
+
+        if (_data_types_by_name.find(morph._base) == _data_types_by_name.end()) {
+          gobj_cat.warning()
+            << "vertex format defines " 
+            << *data_type->get_name() << " but does not define "
+            << *morph._base << "\n";
+        } else {
+          _morphs.push_back(morph);
+        }
+      }
+    }
+  }
+
+
   _is_registered = true;
 }
  

+ 14 - 0
panda/src/gobj/qpgeomVertexFormat.h

@@ -84,6 +84,11 @@ PUBLISHED:
 
   void remove_data_type(const InternalName *name);
 
+  INLINE int get_num_morphs() const;
+  INLINE const InternalName *get_morph_slider(int n) const;
+  INLINE const InternalName *get_morph_base(int n) const;
+  INLINE const InternalName *get_morph_delta(int n) const;
+
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
   void write_with_data(ostream &out, int indent_level, 
@@ -146,6 +151,15 @@ private:
   typedef pmap<const InternalName *, DataTypeRecord> DataTypesByName;
   DataTypesByName _data_types_by_name;
 
+  class MorphRecord {
+  public:
+    CPT(InternalName) _slider;
+    CPT(InternalName) _base;
+    CPT(InternalName) _delta;
+  };
+  typedef pvector<MorphRecord> Morphs;
+  Morphs _morphs;
+
   // This set keeps track of things that need to be told when we
   // destruct, and it is protected by the mutex.
   Mutex _cache_lock;

+ 126 - 0
panda/src/gobj/sliderTable.I

@@ -0,0 +1,126 @@
+// Filename: sliderTable.I
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::is_registered
+//       Access: Published
+//  Description: Returns true if this table has been registered.
+//               Once it has been registered, the set of sliders in
+//               a SliderTable may not be further modified; but
+//               it must be registered before it can be assigned to a
+//               Geom.
+////////////////////////////////////////////////////////////////////
+INLINE bool SliderTable::
+is_registered() const {
+  return _is_registered;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::register_table
+//       Access: Published, Static
+//  Description: Registers a SliderTable for use.  This is
+//               similar to GeomVertexFormat::register_format().  Once
+//               registered, a SliderTable may no longer be
+//               modified (although the individual VertexSlider
+//               objects may modify their reported sliders).
+//
+//               This must be called before a table may be used in a
+//               Geom.  After this call, you should discard the
+//               original pointer you passed in (which may or may not
+//               now be invalid) and let its reference count decrement
+//               normally; you should use only the returned value from
+//               this point on.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(SliderTable) SliderTable::
+register_table(SliderTable *table) {
+  // We don't actually bother adding the table object to a registry.
+  // This means there may be multiple copies of identical registered
+  // SliderTables.  Big deal.  We can always go back and make a
+  // registry later if we really need it.
+  if (table->is_registered()) {
+    return table;
+  }
+
+  table->do_register();
+  return table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::get_slider
+//       Access: Published
+//  Description: Returns the slider with the indicated name, or NULL
+//               if no slider in the table has that name.
+////////////////////////////////////////////////////////////////////
+INLINE const VertexSlider *SliderTable::
+get_slider(const InternalName *name) const {
+  Sliders::const_iterator si;
+  si = _sliders.find(name);
+  if (si != _sliders.end()) {
+    return (*si).second;
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::get_modified
+//       Access: Published
+//  Description: Returns a sequence number that's guaranteed to change
+//               at least when any VertexSliders in the table
+//               change.  (However, this is only true for a registered
+//               table.  An unregistered table may or may not
+//               reflect an update here when a VertexSlider
+//               changes.)
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq SliderTable::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::update_modified
+//       Access: Private
+//  Description: Called internally whenever a nested VertexSlider
+//               reports that it has been modified.
+////////////////////////////////////////////////////////////////////
+INLINE void SliderTable::
+update_modified(UpdateSeq modified) {
+  CDWriter cdata(_cycler);
+  cdata->_modified = modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SliderTable::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SliderTable::CData::
+CData(const SliderTable::CData &copy) :
+  _modified(copy._modified)
+{
+}

+ 249 - 0
panda/src/gobj/sliderTable.cxx

@@ -0,0 +1,249 @@
+// Filename: sliderTable.cxx
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "sliderTable.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle SliderTable::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+SliderTable::
+SliderTable() :
+  _is_registered(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+SliderTable::
+SliderTable(const SliderTable &copy) :
+  _is_registered(false),
+  _sliders(copy._sliders)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+operator = (const SliderTable &copy) {
+  nassertv(!_is_registered);
+  _sliders = copy._sliders;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+SliderTable::
+~SliderTable() {
+  if (_is_registered) {
+    do_unregister();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::remove_slider
+//       Access: Published
+//  Description: Removes the named slider.  Only valid for
+//               unregistered tables.
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+remove_slider(const InternalName *name) {
+  nassertv(!_is_registered);
+
+  Sliders::iterator si = _sliders.find(name);
+  if (si != _sliders.end()) {
+    _sliders.erase(si);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::add_slider
+//       Access: Published
+//  Description: Adds a new slider to the table, or replaces an
+//               existing slider with the same name.  Only valid for
+//               unregistered tables.
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+add_slider(VertexSlider *slider) {
+  nassertv(!_is_registered);
+
+  _sliders[slider->get_name()] = slider;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+write(ostream &out) const {
+  Sliders::const_iterator si;
+  for (si = _sliders.begin(); si != _sliders.end(); ++si) {
+    out << *(*si).second << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::do_register
+//       Access: Private
+//  Description: Called internally when the table is registered.
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+do_register() {
+  nassertv(!_is_registered);
+
+  Sliders::iterator si;
+  for (si = _sliders.begin(); si != _sliders.end(); ++si) {
+    bool inserted = (*si).second->_tables.insert(this).second;
+    nassertv(inserted);
+  }
+  _is_registered = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::do_unregister
+//       Access: Private
+//  Description: Called internally when the table is unregistered
+//               (i.e. right before destruction).
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+do_unregister() {
+  nassertv(_is_registered);
+
+  Sliders::iterator si;
+  for (si = _sliders.begin(); si != _sliders.end(); ++si) {
+    (*si).second->_tables.erase(this);
+  }
+  _is_registered = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               SliderTable.
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type SliderTable is encountered
+//               in the Bam file.  It should create the SliderTable
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *SliderTable::
+make_from_bam(const FactoryParams &params) {
+  SliderTable *object = new SliderTable;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new SliderTable.
+////////////////////////////////////////////////////////////////////
+void SliderTable::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *SliderTable::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void SliderTable::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::CData::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int SliderTable::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new SliderTable.
+////////////////////////////////////////////////////////////////////
+void SliderTable::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 126 - 0
panda/src/gobj/sliderTable.h

@@ -0,0 +1,126 @@
+// Filename: sliderTable.h
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef SLIDERTABLE_H
+#define SLIDERTABLE_H
+
+#include "pandabase.h"
+#include "vertexSlider.h"
+#include "typedWritableReferenceCount.h"
+#include "pointerTo.h"
+#include "pmap.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : SliderTable
+// Description : Stores the total set of VertexSliders that the
+//               vertices in a particular GeomVertexData object might
+//               depend on.
+//
+//               This is similar to a TransformPalette, but it stores
+//               VertexSliders instead of VertexTransforms, and it
+//               stores them by name instead of by index number.
+//               Also, it is only used when animating vertices on the
+//               CPU, since GPU's don't support morphs at this point
+//               in time.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA SliderTable : public TypedWritableReferenceCount {
+PUBLISHED:
+  SliderTable();
+  SliderTable(const SliderTable &copy);
+  void operator = (const SliderTable &copy);
+  virtual ~SliderTable();
+
+  INLINE bool is_registered() const;
+  INLINE static CPT(SliderTable) register_table(SliderTable *table);
+
+  INLINE const VertexSlider *get_slider(const InternalName *name) const;
+  INLINE UpdateSeq get_modified() const;
+
+  void remove_slider(const InternalName *name);
+  void add_slider(VertexSlider *slider);
+
+  void write(ostream &out) const;
+
+private:
+  void do_register();
+  void do_unregister();
+  INLINE void update_modified(UpdateSeq modified);
+
+private:
+  bool _is_registered;
+
+  typedef pmap< const InternalName *, PT(VertexSlider) > Sliders;
+  Sliders _sliders;
+
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    UpdateSeq _modified;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "SliderTable",
+                  TypedWritableReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class VertexSlider;
+};
+
+INLINE ostream &operator << (ostream &out, const SliderTable &obj);
+
+#include "sliderTable.I"
+
+#endif

+ 2 - 2
panda/src/gobj/transformPalette.I

@@ -105,9 +105,9 @@ get_modified() const {
 //               reports that it has been modified.
 ////////////////////////////////////////////////////////////////////
 INLINE void TransformPalette::
-update_modified(UpdateSeq) {
+update_modified(UpdateSeq modified) {
   CDWriter cdata(_cycler);
-  ++(cdata->_modified);
+  cdata->_modified = modified;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 52 - 0
panda/src/gobj/userVertexSlider.I

@@ -0,0 +1,52 @@
+// Filename: userVertexSlider.I
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::set_slider
+//       Access: Published
+//  Description: Stores the indicated slider value.
+////////////////////////////////////////////////////////////////////
+INLINE void UserVertexSlider::
+set_slider(float slider) {
+  CDWriter cdata(_cycler);
+  cdata->_slider = slider;
+  mark_modified();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE UserVertexSlider::CData::
+CData() :
+  _slider(0.0f)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE UserVertexSlider::CData::
+CData(const UserVertexSlider::CData &copy) :
+  _slider(copy._slider)
+{
+}

+ 145 - 0
panda/src/gobj/userVertexSlider.cxx

@@ -0,0 +1,145 @@
+// Filename: userVertexSlider.cxx
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "userVertexSlider.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle UserVertexSlider::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+UserVertexSlider::
+UserVertexSlider(const string &name) :
+  VertexSlider(InternalName::make(name))
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+UserVertexSlider::
+UserVertexSlider(const InternalName *name) :
+  VertexSlider(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::get_slider
+//       Access: Published, Virtual
+//  Description: Returns the current slider value.
+////////////////////////////////////////////////////////////////////
+float UserVertexSlider::
+get_slider() const {
+  CDReader cdata(_cycler);
+  return cdata->_slider;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *UserVertexSlider::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               UserVertexSlider.
+////////////////////////////////////////////////////////////////////
+void UserVertexSlider::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void UserVertexSlider::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  VertexSlider::write_datagram(manager, dg);
+
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type UserVertexSlider is encountered
+//               in the Bam file.  It should create the UserVertexSlider
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *UserVertexSlider::
+make_from_bam(const FactoryParams &params) {
+  UserVertexSlider *object = new UserVertexSlider("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new UserVertexSlider.
+////////////////////////////////////////////////////////////////////
+void UserVertexSlider::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  VertexSlider::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void UserVertexSlider::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexSlider::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new UserVertexSlider.
+////////////////////////////////////////////////////////////////////
+void UserVertexSlider::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 91 - 0
panda/src/gobj/userVertexSlider.h

@@ -0,0 +1,91 @@
+// Filename: userVertexSlider.h
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef USERVERTEXSLIDER_H
+#define USERVERTEXSLIDER_H
+
+#include "pandabase.h"
+#include "vertexSlider.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : UserVertexSlider
+// Description : This is a specialization on VertexSlider that
+//               allows the user to specify any arbitrary slider
+//               valie he likes.  This is rarely used except for
+//               testing.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA UserVertexSlider : public VertexSlider {
+PUBLISHED:
+  UserVertexSlider(const string &name);
+  UserVertexSlider(const InternalName *name);
+
+  INLINE void set_slider(float slider);
+  virtual float get_slider() const;
+
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    float _slider;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    VertexSlider::init_type();
+    register_type(_type_handle, "UserVertexSlider",
+                  VertexSlider::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "userVertexSlider.I"
+
+#endif

+ 1 - 1
panda/src/gobj/userVertexTransform.cxx

@@ -51,7 +51,7 @@ get_matrix(LMatrix4f &matrix) const {
 ////////////////////////////////////////////////////////////////////
 void UserVertexTransform::
 output(ostream &out) const {
-  out << get_name();
+  out << get_type() << " " << get_name();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 70 - 0
panda/src/gobj/vertexSlider.I

@@ -0,0 +1,70 @@
+// Filename: vertexSlider.I
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::get_name
+//       Access: Published
+//  Description: Returns the name of this particular slider.  Every
+//               unique blend shape within a particular Geom must be
+//               identified with a different name, which is shared by
+//               the slider that controls it.
+////////////////////////////////////////////////////////////////////
+INLINE const InternalName *VertexSlider::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::get_modified
+//       Access: Published
+//  Description: Returns a sequence number that's guaranteed to change
+//               at least every time the value reported by
+//               get_slider() changes.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq VertexSlider::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE VertexSlider::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE VertexSlider::CData::
+CData(const VertexSlider::CData &copy) :
+  _modified(copy._modified)
+{
+}
+
+INLINE ostream &
+operator << (ostream &out, const VertexSlider &obj) {
+  obj.output(out);
+  return out;
+}

+ 154 - 0
panda/src/gobj/vertexSlider.cxx

@@ -0,0 +1,154 @@
+// Filename: vertexSlider.cxx
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "vertexSlider.h"
+#include "vertexTransform.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "indent.h"
+
+TypeHandle VertexSlider::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VertexSlider::
+VertexSlider(const InternalName *name) : _name(name) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VertexSlider::
+~VertexSlider() {
+  // We shouldn't destruct while any SliderTables are holding our
+  // pointer.
+  nassertv(_tables.empty());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexSlider::
+output(ostream &out) const {
+  out << get_type() << " " << get_name();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexSlider::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) 
+    << *this << " = " << get_slider() << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::mark_modified
+//       Access: Protected
+//  Description: Intended to be called by a derived class whenever the
+//               reported transform might have changed.  Without
+//               calling this method, changes to get_slider() may not
+//               be propagated through the system.
+////////////////////////////////////////////////////////////////////
+void VertexSlider::
+mark_modified() {
+  CDWriter cdata(_cycler);
+  cdata->_modified = VertexTransform::get_next_modified();
+  
+  Tables::iterator ti;
+  for (ti = _tables.begin(); ti != _tables.end(); ++ti) {
+    (*ti)->update_modified(cdata->_modified);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void VertexSlider::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new VertexSlider.
+////////////////////////////////////////////////////////////////////
+void VertexSlider::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *VertexSlider::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void VertexSlider::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::CData::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int VertexSlider::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexSlider::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new VertexSlider.
+////////////////////////////////////////////////////////////////////
+void VertexSlider::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 119 - 0
panda/src/gobj/vertexSlider.h

@@ -0,0 +1,119 @@
+// Filename: vertexSlider.h
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef VERTEXSLIDER_H
+#define VERTEXSLIDER_H
+
+#include "pandabase.h"
+#include "typedWritableReferenceCount.h"
+#include "internalName.h"
+#include "updateSeq.h"
+#include "pset.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+class SliderTable;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VertexSlider
+// Description : This is an abstract base class that retains some
+//               slider value, which is a linear value that typically
+//               ranges from 0.0 to 1.0, and is used to control the
+//               animation of morphs (blend shapes).
+//
+//               It is similar to VertexTransform, which keeps a full
+//               4x4 transform matrix, but the VertexSlider only keeps
+//               a single float value.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA VertexSlider : public TypedWritableReferenceCount {
+PUBLISHED:
+  VertexSlider(const InternalName *name);
+  virtual ~VertexSlider();
+
+  INLINE const InternalName *get_name() const;
+
+  virtual float get_slider() const=0;
+  INLINE UpdateSeq get_modified() const;
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+protected:
+  void mark_modified();
+
+private:
+  CPT(InternalName) _name;
+
+  typedef pset<SliderTable *> Tables;
+  Tables _tables;
+
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    UpdateSeq _modified;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+  static PipelineCycler<CData> _global_cycler;
+  static UpdateSeq _next_modified;
+
+public:
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "VertexSlider",
+                  TypedWritableReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class SliderTable;
+};
+
+INLINE ostream &operator << (ostream &out, const VertexSlider &obj);
+
+#include "vertexSlider.I"
+
+#endif