Browse Source

beginning vertex animation in qpgeom

David Rose 21 years ago
parent
commit
bd820acd49
40 changed files with 3113 additions and 142 deletions
  1. 3 3
      panda/src/dxgsg8/dxGeomMunger8.cxx
  2. 7 7
      panda/src/egg2pg/eggLoader.cxx
  3. 1 1
      panda/src/express/pointerToArray.I
  4. 20 20
      panda/src/framework/windowFramework.cxx
  5. 1 1
      panda/src/glstuff/glGeomMunger_src.I
  6. 1 1
      panda/src/glstuff/glGeomMunger_src.cxx
  7. 10 2
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  8. 18 3
      panda/src/gobj/Sources.pp
  9. 11 0
      panda/src/gobj/config_gobj.cxx
  10. 5 0
      panda/src/gobj/gobj_composite2.cxx
  11. 13 0
      panda/src/gobj/internalName.I
  12. 1 0
      panda/src/gobj/internalName.cxx
  13. 2 0
      panda/src/gobj/internalName.h
  14. 1 1
      panda/src/gobj/qpgeom.cxx
  15. 0 1
      panda/src/gobj/qpgeomLines.cxx
  16. 0 3
      panda/src/gobj/qpgeomLinestrips.cxx
  17. 35 0
      panda/src/gobj/qpgeomVertexData.I
  18. 434 22
      panda/src/gobj/qpgeomVertexData.cxx
  19. 23 1
      panda/src/gobj/qpgeomVertexData.h
  20. 20 9
      panda/src/gobj/qpgeomVertexDataType.cxx
  21. 4 3
      panda/src/gobj/qpgeomVertexDataType.h
  22. 28 28
      panda/src/gobj/qpgeomVertexFormat.cxx
  23. 53 25
      panda/src/gobj/qpgeomVertexIterator.I
  24. 18 11
      panda/src/gobj/qpgeomVertexIterator.h
  25. 277 0
      panda/src/gobj/transformBlend.I
  26. 287 0
      panda/src/gobj/transformBlend.cxx
  27. 123 0
      panda/src/gobj/transformBlend.h
  28. 80 0
      panda/src/gobj/transformBlendPalette.I
  29. 269 0
      panda/src/gobj/transformBlendPalette.cxx
  30. 141 0
      panda/src/gobj/transformBlendPalette.h
  31. 131 0
      panda/src/gobj/transformPalette.I
  32. 259 0
      panda/src/gobj/transformPalette.cxx
  33. 127 0
      panda/src/gobj/transformPalette.h
  34. 63 0
      panda/src/gobj/userVertexTransform.I
  35. 144 0
      panda/src/gobj/userVertexTransform.cxx
  36. 96 0
      panda/src/gobj/userVertexTransform.h
  37. 71 0
      panda/src/gobj/vertexTransform.I
  38. 217 0
      panda/src/gobj/vertexTransform.cxx
  39. 118 0
      panda/src/gobj/vertexTransform.h
  40. 1 0
      panda/src/pgraph/cullableObject.cxx

+ 3 - 3
panda/src/dxgsg8/dxGeomMunger8.cxx

@@ -45,7 +45,7 @@ munge_format_impl(const qpGeomVertexFormat *orig) {
 
   if (vertex_type != (const qpGeomVertexDataType *)NULL) {
     new_array_format->add_data_type
-      (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float);
+      (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32);
   } else {
     // If we don't have a vertex type, not much we can do.
     return orig;
@@ -53,7 +53,7 @@ munge_format_impl(const qpGeomVertexFormat *orig) {
 
   if (normal_type != (const qpGeomVertexDataType *)NULL) {
     new_array_format->add_data_type
-      (InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float);
+      (InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32);
   }
 
   if (color_type != (const qpGeomVertexDataType *)NULL) {
@@ -67,7 +67,7 @@ munge_format_impl(const qpGeomVertexFormat *orig) {
   if (texcoord_type != (const qpGeomVertexDataType *)NULL) {
     new_array_format->add_data_type
       (InternalName::get_texcoord(), texcoord_type->get_num_values(),
-       qpGeomVertexDataType::NT_float);
+       qpGeomVertexDataType::NT_float32);
   }
 
   PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(new_array_format);

+ 7 - 7
panda/src/egg2pg/eggLoader.cxx

@@ -1904,11 +1904,11 @@ make_vertex_data(const EggRenderState *render_state,
   PT(qpGeomVertexArrayFormat) array_format = new qpGeomVertexArrayFormat;
   array_format->add_data_type
     (InternalName::get_vertex(), vertex_pool->get_num_dimensions(),
-     qpGeomVertexDataType::NT_float);
+     qpGeomVertexDataType::NT_float32);
 
   if (vertex_pool->has_normals()) {
     array_format->add_data_type
-      (InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float);
+      (InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32);
   }
 
   if (vertex_pool->has_colors()) {
@@ -1925,7 +1925,7 @@ make_vertex_data(const EggRenderState *render_state,
       name = string();
     }
     PT(InternalName) iname = InternalName::get_texcoord_name(name);
-    array_format->add_data_type(iname, 2, qpGeomVertexDataType::NT_float);
+    array_format->add_data_type(iname, 2, qpGeomVertexDataType::NT_float32);
   }
 
   CPT(qpGeomVertexFormat) format = 
@@ -1943,16 +1943,16 @@ make_vertex_data(const EggRenderState *render_state,
     gvi.set_vertex(vertex->get_index());
 
     gvi.set_data_type(InternalName::get_vertex());
-    gvi.set_data4(LCAST(float, vertex->get_pos4() * transform));
+    gvi.set_data4f(LCAST(float, vertex->get_pos4() * transform));
 
     if (vertex->has_normal()) {
       gvi.set_data_type(InternalName::get_normal());
-      gvi.set_data3(LCAST(float, vertex->get_normal() * transform));
+      gvi.set_data3f(LCAST(float, vertex->get_normal() * transform));
     }
 
     if (vertex->has_color()) {
       gvi.set_data_type(InternalName::get_color());
-      gvi.set_data4(vertex->get_color());
+      gvi.set_data4f(vertex->get_color());
     }
 
     EggVertex::const_uv_iterator uvi;
@@ -1973,7 +1973,7 @@ make_vertex_data(const EggRenderState *render_state,
         uv = uv * (*buv).second->get_transform();
       }
 
-      gvi.set_data2(LCAST(float, uv));
+      gvi.set_data2f(LCAST(float, uv));
     }
   }
 

+ 1 - 1
panda/src/express/pointerToArray.I

@@ -428,7 +428,7 @@ p() const {
 template<class Element>
 INLINE pvector<Element> &PointerToArray<Element>::
 v() const {
-  nassertd((this->_void_ptr) != NULL) {
+  if ((this->_void_ptr) == NULL) {
     ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
   }
   return *((To *)(this->_void_ptr));

+ 20 - 20
panda/src/framework/windowFramework.cxx

@@ -640,21 +640,21 @@ load_default_model(const NodePath &parent) {
     qpGeomVertexIterator color(vdata, InternalName::get_color());
     qpGeomVertexIterator texcoord(vdata, InternalName::get_texcoord());
 
-    vertex.set_data3(Vertexf::rfu(0.0, 0.0, 0.0));
-    vertex.set_data3(Vertexf::rfu(1.0, 0.0, 0.0));
-    vertex.set_data3(Vertexf::rfu(0.0, 0.0, 1.0));
+    vertex.set_data3f(Vertexf::rfu(0.0, 0.0, 0.0));
+    vertex.set_data3f(Vertexf::rfu(1.0, 0.0, 0.0));
+    vertex.set_data3f(Vertexf::rfu(0.0, 0.0, 1.0));
 
-    normal.set_data3(Normalf::back());
-    normal.set_data3(Normalf::back());
-    normal.set_data3(Normalf::back());
+    normal.set_data3f(Normalf::back());
+    normal.set_data3f(Normalf::back());
+    normal.set_data3f(Normalf::back());
 
-    color.set_data4(0.5, 0.5, 1.0, 1.0);
-    color.set_data4(0.5, 0.5, 1.0, 1.0);
-    color.set_data4(0.5, 0.5, 1.0, 1.0);
+    color.set_data4f(0.5, 0.5, 1.0, 1.0);
+    color.set_data4f(0.5, 0.5, 1.0, 1.0);
+    color.set_data4f(0.5, 0.5, 1.0, 1.0);
 
-    texcoord.set_data2(0.0, 0.0);
-    texcoord.set_data2(1.0, 0.0);
-    texcoord.set_data2(0.0, 1.0);
+    texcoord.set_data2f(0.0, 0.0);
+    texcoord.set_data2f(1.0, 0.0);
+    texcoord.set_data2f(0.0, 1.0);
     
     PT(qpGeomTriangles) tri = new qpGeomTriangles(qpGeomUsageHint::UH_static);
     tri->add_consecutive_vertices(0, 3);
@@ -1065,15 +1065,15 @@ load_image_as_model(const Filename &filename) {
     qpGeomVertexIterator vertex(vdata, InternalName::get_vertex());
     qpGeomVertexIterator texcoord(vdata, InternalName::get_texcoord());
 
-    vertex.set_data3(Vertexf::rfu(left, 0.02f, top));
-    vertex.set_data3(Vertexf::rfu(left, 0.02f, bottom));
-    vertex.set_data3(Vertexf::rfu(right, 0.02f, top));
-    vertex.set_data3(Vertexf::rfu(right, 0.02f, bottom));
+    vertex.set_data3f(Vertexf::rfu(left, 0.02f, top));
+    vertex.set_data3f(Vertexf::rfu(left, 0.02f, bottom));
+    vertex.set_data3f(Vertexf::rfu(right, 0.02f, top));
+    vertex.set_data3f(Vertexf::rfu(right, 0.02f, bottom));
     
-    texcoord.set_data2(0.0f, 1.0f);
-    texcoord.set_data2(0.0f, 0.0f);
-    texcoord.set_data2(1.0f, 1.0f);
-    texcoord.set_data2(1.0f, 0.0f);
+    texcoord.set_data2f(0.0f, 1.0f);
+    texcoord.set_data2f(0.0f, 0.0f);
+    texcoord.set_data2f(1.0f, 1.0f);
+    texcoord.set_data2f(1.0f, 0.0f);
     
     PT(qpGeomTristrips) strip = new qpGeomTristrips(qpGeomUsageHint::UH_static);
     strip->add_consecutive_vertices(0, 4);

+ 1 - 1
panda/src/glstuff/glGeomMunger_src.I

@@ -24,7 +24,7 @@
 ////////////////////////////////////////////////////////////////////
 INLINE CLP(GeomMunger)::
 CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
-  ColorMunger(gsg, state, 4, qpGeomVertexDataType::NT_uint8),
+  ColorMunger(gsg, state, 4, qpGeomVertexDataType::NT_ufloat8),
   _gsg(gsg),
   _texture(state->get_texture()),
   _tex_gen(state->get_tex_gen())

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

@@ -60,7 +60,7 @@ munge_format_impl(const qpGeomVertexFormat *orig) {
 
     // Replace the existing color format with the new format.
     new_array_format->add_data_type
-      (InternalName::get_color(), 4, qpGeomVertexDataType::NT_uint8,
+      (InternalName::get_color(), 4, qpGeomVertexDataType::NT_ufloat8,
        color_type->get_start());
 
     format = qpGeomVertexFormat::register_format(new_format);

+ 10 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2079,6 +2079,8 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger,
     case qpGeomPrimitive::PT_lines:
       setup_antialias_line();
       break;
+    case qpGeomPrimitive::PT_none:
+      break;
     }
   }
 
@@ -4428,11 +4430,17 @@ upload_texture_image(CLP(TextureContext) *gtc,
 GLenum CLP(GraphicsStateGuardian)::
 get_numeric_type(qpGeomVertexDataType::NumericType numeric_type) {
   switch (numeric_type) {
-  case qpGeomVertexDataType::NT_uint8:
+  case qpGeomVertexDataType::NT_uint16:
+    return GL_UNSIGNED_SHORT;
+
+  case qpGeomVertexDataType::NT_ufloat8:
     return GL_UNSIGNED_BYTE;
     
-  case qpGeomVertexDataType::NT_float:
+  case qpGeomVertexDataType::NT_float32:
     return GL_FLOAT;
+
+  case qpGeomVertexDataType::NT_packed_argb:
+    break;
   }
 
   GLCAT.error()

+ 18 - 3
panda/src/gobj/Sources.pp

@@ -49,7 +49,12 @@
     textureContext.I textureContext.h \
     texturePool.I texturePool.h \
     textureStage.I textureStage.h \
-    vertexBufferContext.I vertexBufferContext.h
+    transformBlend.I transformBlend.h \
+    transformBlendPalette.I transformBlendPalette.h \
+    transformPalette.I transformPalette.h \
+    userVertexTransform.I userVertexTransform.h \
+    vertexBufferContext.I vertexBufferContext.h \
+    vertexTransform.I vertexTransform.h
     
   #define INCLUDED_SOURCES \
     boundedObject.cxx \
@@ -85,7 +90,12 @@
     lens.cxx  \
     savedContext.cxx texture.cxx textureContext.cxx texturePool.cxx \
     textureStage.cxx \
-    vertexBufferContext.cxx
+    transformBlend.cxx \
+    transformBlendPalette.cxx \
+    transformPalette.cxx \
+    userVertexTransform.cxx \
+    vertexBufferContext.cxx \
+    vertexTransform.cxx
 
   #define INSTALL_HEADERS \
     boundedObject.I boundedObject.h \
@@ -127,7 +137,12 @@
     textureContext.I textureContext.h \
     texturePool.I texturePool.h \
     textureStage.I textureStage.h \
-    vertexBufferContext.I vertexBufferContext.h
+    transformBlend.I transformBlend.h \
+    transformBlendPalette.I transformBlendPalette.h \
+    transformPalette.I transformPalette.h \
+    userVertexTransform.I userVertexTransform.h \
+    vertexBufferContext.I vertexBufferContext.h \
+    vertexTransform.I vertexTransform.h
 
 
   #define IGATESCAN all

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

@@ -43,6 +43,10 @@
 #include "texture.h"
 #include "textureStage.h"
 #include "textureContext.h"
+#include "transformBlendPalette.h"
+#include "transformPalette.h"
+#include "userVertexTransform.h"
+#include "vertexTransform.h"
 #include "geomContext.h"
 #include "vertexBufferContext.h"
 #include "indexBufferContext.h"
@@ -211,6 +215,10 @@ ConfigureFn(config_gobj) {
   Texture::init_type();
   dDrawable::init_type();
   TextureStage::init_type();
+  TransformBlendPalette::init_type();
+  TransformPalette::init_type();
+  UserVertexTransform::init_type();
+  VertexTransform::init_type();
   InternalName::init_type();
 
   //Registration of writeable object's creation
@@ -242,6 +250,9 @@ ConfigureFn(config_gobj) {
   PerspectiveLens::register_with_read_factory();
   Texture::register_with_read_factory();
   TextureStage::register_with_read_factory();
+  TransformBlendPalette::register_with_read_factory();
+  TransformPalette::register_with_read_factory();
+  UserVertexTransform::register_with_read_factory();
   InternalName::register_with_read_factory();
 }
 

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

@@ -16,4 +16,9 @@
 #include "textureContext.cxx"
 #include "texturePool.cxx"
 #include "textureStage.cxx"
+#include "transformBlend.cxx"
+#include "transformBlendPalette.cxx"
+#include "transformPalette.cxx"
+#include "userVertexTransform.cxx"
 #include "vertexBufferContext.cxx"
+#include "vertexTransform.cxx"

+ 13 - 0
panda/src/gobj/internalName.I

@@ -178,6 +178,19 @@ get_color() {
   return _color;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::get_transform_blend
+//       Access: Published, Static
+//  Description: Returns the standard InternalName "transform_blend".
+////////////////////////////////////////////////////////////////////
+INLINE PT(InternalName) InternalName::
+get_transform_blend() {
+  if (_transform_blend == (InternalName *)NULL) {
+    _transform_blend = InternalName::make("transform_blend");
+  }
+  return _transform_blend;
+}
+
 INLINE ostream &
 operator << (ostream &out, const InternalName &tcn) {
   tcn.output(out);

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

@@ -32,6 +32,7 @@ PT(InternalName) InternalName::_tangent;
 PT(InternalName) InternalName::_binormal;
 PT(InternalName) InternalName::_texcoord;
 PT(InternalName) InternalName::_color;
+PT(InternalName) InternalName::_transform_blend;
 
 TypeHandle InternalName::_type_handle;
 TypeHandle InternalName::_texcoord_type_handle;

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

@@ -67,6 +67,7 @@ PUBLISHED:
   INLINE static PT(InternalName) get_texcoord();
   INLINE static PT(InternalName) get_texcoord_name(const string &name);
   INLINE static PT(InternalName) get_color();
+  INLINE static PT(InternalName) get_transform_blend();
 
 private:
   PT(InternalName) _parent;
@@ -84,6 +85,7 @@ private:
   static PT(InternalName) _binormal;
   static PT(InternalName) _texcoord;
   static PT(InternalName) _color;
+  static PT(InternalName) _transform_blend;
   
 public:
   // Datagram stuff

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

@@ -397,7 +397,7 @@ draw(GraphicsStateGuardianBase *gsg, const qpGeomMunger *munger,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::get_next_modified
-//       Access: Public
+//       Access: Public, Static
 //  Description: Returns a monotonically increasing sequence.  Each
 //               time this is called, a new sequence number is
 //               returned, higher than the previous value.

+ 0 - 1
panda/src/gobj/qpgeomLines.cxx

@@ -114,7 +114,6 @@ CPTA_ushort qpGeomLines::
 rotate_impl() const {
   // To rotate lines, we just move reverse the pairs of vertices.
   CPTA_ushort vertices = get_vertices();
-  ShadeModel shade_model = get_shade_model();
 
   PTA_ushort new_vertices;
   new_vertices.reserve(vertices.size());

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

@@ -151,12 +151,9 @@ rotate_impl() const {
   CPTA_int::const_iterator ei;
   for (ei = ends.begin(); ei != ends.end(); ++ei) {
     int end = (*ei);
-    int num_vertices = end - begin;
-
     for (int vi = end - 1; vi >= begin; --vi) {
       new_vertices.push_back(vertices[vi]);
     }
-
     begin = end;
   }
   nassertr(new_vertices.size() == vertices.size(), vertices);

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

@@ -91,6 +91,38 @@ get_array(int i) const {
   return cdata->_arrays[i];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_transform_blend_palette
+//       Access: Published
+//  Description: Returns a const pointer to the TransformBlendPalette
+//               assigned to this data.  Vertices within the table
+//               will index into this palette to indicate their
+//               dynamic skinning information; this table is used when
+//               the vertex animation is to be performed by the CPU
+//               (but also see get_transform_palette()).
+//
+//               This will return NULL if the vertex data does not
+//               have a TransformBlendPalette assigned (which implies
+//               the vertices will not be animated by the CPU).
+////////////////////////////////////////////////////////////////////
+INLINE const TransformBlendPalette *qpGeomVertexData::
+get_transform_blend_palette() const {
+  CDReader cdata(_cycler);
+  return cdata->_transform_blend_palette;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::clear_transform_blend_palette
+//       Access: Published
+//  Description: Sets the TransformBlendPalette pointer to NULL,
+//               removing the palette from the vertex data.  This
+//               disables CPU-driven vertex animation.
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomVertexData::
+clear_transform_blend_palette() {
+  set_transform_blend_palette(NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::get_modified
 //       Access: Published
@@ -121,6 +153,9 @@ CData() {
 INLINE qpGeomVertexData::CData::
 CData(const qpGeomVertexData::CData &copy) :
   _arrays(copy._arrays),
+  _transform_blend_palette(copy._transform_blend_palette),
+  _computed_vertices(copy._computed_vertices),
+  _computed_vertices_modified(copy._computed_vertices_modified),
   _modified(copy._modified)
 {
 }

+ 434 - 22
panda/src/gobj/qpgeomVertexData.cxx

@@ -28,6 +28,7 @@ TypeHandle qpGeomVertexData::_type_handle;
 PStatCollector qpGeomVertexData::_convert_pcollector("Cull:Munge:Convert");
 PStatCollector qpGeomVertexData::_scale_color_pcollector("Cull:Munge:Scale color");
 PStatCollector qpGeomVertexData::_set_color_pcollector("Cull:Munge:Set color");
+PStatCollector qpGeomVertexData::_compute_vertices_pcollector("Cull:Compute vertices");
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::Default Constructor
@@ -137,6 +138,7 @@ clear_vertices() {
     (*ai)->clear_vertices();
   }
   cdata->_modified = qpGeom::get_next_modified();
+  cdata->_computed_vertices.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -160,6 +162,7 @@ modify_array(int i) {
     cdata->_arrays[i] = new qpGeomVertexArrayData(*cdata->_arrays[i]);
   }
   cdata->_modified = qpGeom::get_next_modified();
+  cdata->_computed_vertices_modified = UpdateSeq();
 
   return cdata->_arrays[i];
 }
@@ -178,6 +181,48 @@ set_array(int i, const qpGeomVertexArrayData *array) {
   nassertv(i >= 0 && i < (int)cdata->_arrays.size());
   cdata->_arrays[i] = (qpGeomVertexArrayData *)array;
   cdata->_modified = qpGeom::get_next_modified();
+  cdata->_computed_vertices_modified = UpdateSeq();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::modify_transform_blend_palette
+//       Access: Published
+//  Description: Returns a modifiable pointer to the current
+//               TransformBlendPalette on this vertex data, if any, or
+//               NULL if there is not a TransformBlendPalette.  See
+//               get_transform_blend_palette().
+////////////////////////////////////////////////////////////////////
+TransformBlendPalette *qpGeomVertexData::
+modify_transform_blend_palette() {
+  // Perform copy-on-write: if the reference count on the palette is
+  // greater than 1, assume some other GeomVertexData has the same
+  // pointer, so make a copy of it first.
+  CDWriter cdata(_cycler);
+
+  if (cdata->_transform_blend_palette->get_ref_count() > 1) {
+    cdata->_transform_blend_palette = new TransformBlendPalette(*cdata->_transform_blend_palette);
+  }
+  cdata->_modified = qpGeom::get_next_modified();
+  cdata->_computed_vertices_modified = UpdateSeq();
+
+  return cdata->_transform_blend_palette;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::set_transform_blend_palette
+//       Access: Published
+//  Description: Replaces the TransformBlendPalette on this vertex
+//               data with the indicated palette.  The length of this
+//               palette should be consistent with the maximum palette
+//               index assigned to the vertices under the
+//               "transform_blend" name.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+set_transform_blend_palette(const TransformBlendPalette *palette) {
+  CDWriter cdata(_cycler);
+  cdata->_transform_blend_palette = (TransformBlendPalette *)palette;
+  cdata->_modified = qpGeom::get_next_modified();
+  cdata->_computed_vertices_modified = UpdateSeq();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -226,6 +271,7 @@ convert_to(const qpGeomVertexFormat *new_format) const {
 
   PT(qpGeomVertexData) new_data = 
     new qpGeomVertexData(new_format, get_usage_hint());
+  new_data->set_transform_blend_palette(get_transform_blend_palette());
 
   pset<int> done_arrays;
 
@@ -319,15 +365,16 @@ scale_color(const LVecBase4f &color_scale, int num_components,
   PStatTimer timer(_scale_color_pcollector);
 
   PT(qpGeomVertexData) new_data = replace_data_type
-    (InternalName::get_color(), num_components, numeric_type);
+    (InternalName::get_color(), num_components, numeric_type,
+     get_usage_hint(), true);
 
   // Now go through and apply the scale, copying it to the new data.
   qpGeomVertexIterator from(this, InternalName::get_color());
   qpGeomVertexIterator to(new_data, InternalName::get_color());
 
   for (int i = 0; i < num_vertices; i++) {
-    Colorf color = from.get_data4();
-    to.set_data4(color[0] * color_scale[0],
+    Colorf color = from.get_data4f();
+    to.set_data4f(color[0] * color_scale[0],
                  color[1] * color_scale[1],
                  color[2] * color_scale[2],
                  color[3] * color_scale[3]);
@@ -359,18 +406,62 @@ set_color(const Colorf &color, int num_components,
   PStatTimer timer(_set_color_pcollector);
 
   PT(qpGeomVertexData) new_data = replace_data_type
-    (InternalName::get_color(), num_components, numeric_type);
+    (InternalName::get_color(), num_components, numeric_type,
+     get_usage_hint(), true);
 
   // Now go through and set the new color value.
   qpGeomVertexIterator to(new_data, InternalName::get_color());
 
   for (int i = 0; i < num_vertices; i++) {
-    to.set_data4(color);
+    to.set_data4f(color);
   }
 
   return new_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::compute_vertices
+//       Access: Published
+//  Description: Returns a GeomVertexData that represents the results
+//               of computing the vertex animation on the CPU for this
+//               GeomVertexData.
+//
+//               If there is no CPU-defined vertex animation on this
+//               object, this just returns the original object.
+//
+//               If there is vertex animation, but the VertexTransform
+//               values have not changed since last time, this may
+//               return the same pointer it returned previously.  Even
+//               if the VertexTransform values have changed, it may
+//               still return the same pointer, but with its contents
+//               modified (this is preferred, since it allows the
+//               graphics backend to update vertex buffers optimally).
+////////////////////////////////////////////////////////////////////
+CPT(qpGeomVertexData) qpGeomVertexData::
+compute_vertices() const {
+  CDReader cdata(_cycler);
+  if (cdata->_transform_blend_palette == (TransformBlendPalette *)NULL) {
+    // No vertex animation.
+    return this;
+  }
+
+  if (cdata->_computed_vertices == (qpGeomVertexData *)NULL) {
+    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
+    ((qpGeomVertexData *)this)->make_computed_vertices(cdataw);
+    return cdataw->_computed_vertices;
+  } else {
+    UpdateSeq blend_modified = cdata->_transform_blend_palette->get_modified();
+    if (cdata->_computed_vertices_modified == blend_modified) {
+      // No changes.
+      return cdata->_computed_vertices;
+    }
+    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
+    cdataw->_computed_vertices_modified = blend_modified;
+    ((qpGeomVertexData *)this)->update_computed_vertices(cdataw);
+    return cdataw->_computed_vertices;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::replace_data_type
 //       Access: Published
@@ -380,10 +471,16 @@ set_color(const Colorf &color, int num_components,
 //               new table will be added as a new array; if the old
 //               table was interleaved with a previous array, the
 //               previous array will not be repacked.
+//
+//               If num_components is 0, the indicated name is simply
+//               removed from the type, without replacing it with
+//               anything else.
 ////////////////////////////////////////////////////////////////////
 PT(qpGeomVertexData) qpGeomVertexData::
 replace_data_type(const InternalName *name, int num_components,
-                  qpGeomVertexDataType::NumericType numeric_type) const {
+                  qpGeomVertexDataType::NumericType numeric_type,
+                  qpGeomUsageHint::UsageHint usage_hint,
+                  bool keep_animation) const {
   PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(*_format);
 
   // Remove the old description of the type from the format.
@@ -405,13 +502,20 @@ replace_data_type(const InternalName *name, int num_components,
   }
     
   // Now define a new array to contain just the type.
-  PT(qpGeomVertexArrayFormat) type_array_format = 
-    new qpGeomVertexArrayFormat(name, num_components, numeric_type);
-  int new_type_array = new_format->add_array(type_array_format);
-  
+  int new_type_array = -1;
+  if (num_components != 0) {
+    PT(qpGeomVertexArrayFormat) type_array_format = 
+      new qpGeomVertexArrayFormat(name, num_components, numeric_type);
+    new_type_array = new_format->add_array(type_array_format);
+  }
+    
   PT(qpGeomVertexData) new_data = 
     new qpGeomVertexData(qpGeomVertexFormat::register_format(new_format),
-                         get_usage_hint());
+                         usage_hint);
+  if (keep_animation) {
+    new_data->set_transform_blend_palette(get_transform_blend_palette());
+  }
+
 
   int j = 0;
   int num_arrays = get_num_arrays();
@@ -431,14 +535,16 @@ replace_data_type(const InternalName *name, int num_components,
     }
   }
 
-  nassertr(j == new_type_array, new_data);
+  if (new_type_array != -1) {
+    nassertr(j == new_type_array, new_data);
 
-  // For the new type array, we set up a temporary array that has
-  // room for the right number of vertices.
-  PT(qpGeomVertexArrayData) new_array = new qpGeomVertexArrayData
-    (new_format->get_array(j), get_usage_hint());
-  new_array->set_num_vertices(get_num_vertices());
-  new_data->set_array(j, new_array);
+    // For the new type array, we set up a temporary array that has
+    // room for the right number of vertices.
+    PT(qpGeomVertexArrayData) new_array = new qpGeomVertexArrayData
+      (new_format->get_array(j), get_usage_hint());
+    new_array->set_num_vertices(get_num_vertices());
+    new_data->set_array(j, new_array);
+  }
 
   return new_data;
 }
@@ -496,7 +602,26 @@ set_data(int array, const qpGeomVertexDataType *data_type,
   nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
 
   switch (data_type->get_numeric_type()) {
-  case qpGeomVertexDataType::NT_uint8:
+  case qpGeomVertexDataType::NT_uint16:
+    {
+      // Elevate or truncate to the right number of components.
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        *(PN_uint16 *)&array_data[element] = (int)data[i];
+        element += sizeof(PN_uint16);
+        ++i;
+      }
+      while (i < data_type->get_num_values()) {
+        *(PN_uint16 *)&array_data[element] = 0;
+        element += sizeof(PN_uint16);
+        ++i;
+      }
+    }
+      
+    break;
+
+  case qpGeomVertexDataType::NT_ufloat8:
     {
       int i = 0;
       int min_values = min(num_values, data_type->get_num_values());
@@ -528,7 +653,7 @@ set_data(int array, const qpGeomVertexDataType *data_type,
     }
     break;
 
-  case qpGeomVertexDataType::NT_float:
+  case qpGeomVertexDataType::NT_float32:
     if (num_values == 4 && sizeof(float) == sizeof(PN_float32)) {
       // The easy way: we can memcpy the data directly in.
       memcpy(&array_data[element], data, data_type->get_total_bytes());
@@ -577,7 +702,23 @@ get_data(int array, const qpGeomVertexDataType *data_type,
   nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
 
   switch (data_type->get_numeric_type()) {
-  case qpGeomVertexDataType::NT_uint8:
+  case qpGeomVertexDataType::NT_uint16:
+    {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        data[i] = *(PN_uint16 *)&array_data[element];
+        element += sizeof(PN_uint16);
+        ++i;
+      }
+      while (i < num_values) {
+        data[i] = 0;
+        ++i;
+      }
+    }
+    break;
+
+  case qpGeomVertexDataType::NT_ufloat8:
     {
       int i = 0;
       int min_values = min(num_values, data_type->get_num_values());
@@ -607,7 +748,7 @@ get_data(int array, const qpGeomVertexDataType *data_type,
     }
     break;
 
-  case qpGeomVertexDataType::NT_float:
+  case qpGeomVertexDataType::NT_float32:
     if (num_values == data_type->get_num_values() && 
         sizeof(float) == sizeof(PN_float32)) {
       memcpy(data, &array_data[element], num_values * sizeof(PN_float32));
@@ -628,7 +769,194 @@ get_data(int array, const qpGeomVertexDataType *data_type,
         ++i;
       }
     }
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::set_data
+//       Access: Public
+//  Description: Sets the nth vertex to a particular value.  Query the
+//               format to get the array index and data_type
+//               parameters for the particular data type you want to
+//               set.
+//
+//               This flavor of set_data() accepts a generic float
+//               array and a specific number of dimensions.  The new
+//               data will be copied from the num_values elements
+//               of data.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+set_data(int array, const qpGeomVertexDataType *data_type,
+         int vertex, const int *data, int num_values) {
+  int stride = _format->get_array(array)->get_stride();
+  int element = vertex * stride + data_type->get_start();
+
+  {
+    CDReader cdata(_cycler);
+    int array_size = (int)cdata->_arrays[array]->get_data_size_bytes();
+    if (element + data_type->get_total_bytes() > array_size) {
+      // Whoops, we need more vertices!
+      CDWriter cdataw(_cycler, cdata);
+      do_set_num_vertices(vertex + 1, cdataw);
+    }
+  }
+
+  PTA_uchar array_data = modify_array(array)->modify_data();
+  nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
+
+  switch (data_type->get_numeric_type()) {
+  case qpGeomVertexDataType::NT_uint16:
+    {
+      // Elevate or truncate to the right number of components.
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        *(PN_uint16 *)&array_data[element] = data[i];
+        element += sizeof(PN_uint16);
+        ++i;
+      }
+      while (i < data_type->get_num_values()) {
+        *(PN_uint16 *)&array_data[element] = 0;
+        element += sizeof(PN_uint16);
+        ++i;
+      }
+    }
+      
+    break;
+
+  case qpGeomVertexDataType::NT_ufloat8:
+    {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        array_data[element] = data[i];
+        element += 1;
+        ++i;
+      }
+      while (i < data_type->get_num_values()) {
+        array_data[element] = 0;
+        element += 1;
+        ++i;
+      }        
+    }
+    break;
+
+  case qpGeomVertexDataType::NT_packed_argb:
+    {
+      if (num_values == 4) {
+        *(PN_uint32 *)&array_data[element] = pack_argb(data);
+      } else {
+        // Elevate (or truncate) to 4 components.
+        int data4[4];
+        memset(data4, 0, 4 * sizeof(int));
+        memcpy(data4, data, min(4, num_values) * sizeof(int));
+        *(PN_uint32 *)&array_data[element] = pack_argb(data4);
+      }
+    }
+    break;
+
+  case qpGeomVertexDataType::NT_float32:
+    {
+      // Elevate or truncate to the right number of components.
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        *(PN_float32 *)&array_data[element] = data[i];
+        element += sizeof(PN_float32);
+        ++i;
+      }
+      while (i < data_type->get_num_values()) {
+        *(PN_float32 *)&array_data[element] = 0.0f;
+        element += sizeof(PN_float32);
+        ++i;
+      }
+    }
+      
+    break;
+  }
+}
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_data
+//       Access: Public
+//  Description: Returns the data associated with the nth vertex for a
+//               particular value.  Query the format to get the array
+//               index and data_type parameters for the particular
+//               data type you want to get.
+//
+//               This flavor of get_data() copies its data into a
+//               generic float array.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+get_data(int array, const qpGeomVertexDataType *data_type,
+         int vertex, int *data, int num_values) const {
+  CPTA_uchar array_data = get_array(array)->get_data();
+  int stride = _format->get_array(array)->get_stride();
+  int element = vertex * stride + data_type->get_start();
+  nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
+
+  switch (data_type->get_numeric_type()) {
+  case qpGeomVertexDataType::NT_uint16:
+    {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        data[i] = *(PN_uint16 *)&array_data[element];
+        element += sizeof(PN_uint16);
+        ++i;
+      }
+      while (i < num_values) {
+        data[i] = 0;
+        ++i;
+      }
+    }
+    break;
+
+  case qpGeomVertexDataType::NT_ufloat8:
+    {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        int value = *(unsigned char *)&array_data[element];
+        element += 1;
+        data[i] = value;
+        ++i;
+      }
+      while (i < num_values) {
+        data[i] = 0;
+        ++i;
+      }
+    }
+    break;
+
+  case qpGeomVertexDataType::NT_packed_argb:
+    {
+      if (num_values == 4) {
+        unpack_argb(data, *(PN_uint32 *)&array_data[element]);
+      } else {
+        int data4[4];
+        unpack_argb(data4, *(PN_uint32 *)&array_data[element]);
+        memset(data, 0, num_values * sizeof(int));
+        memcpy(data, data4, min(num_values, 4) * sizeof(int));
+      }
+    }
+    break;
+
+  case qpGeomVertexDataType::NT_float32:
+    {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        data[i] = (int)*(PN_float32 *)&array_data[element];
+        element += sizeof(PN_float32);
+        ++i;
+      }
+      while (i < num_values) {
+        data[i] = 0;
+        ++i;
+      }
+    }
     break;
   }
 }
@@ -774,6 +1102,35 @@ unpack_argb(float data[4], unsigned int packed_argb) {
   data[3] = (float)((packed_argb >> 24) & 0xff) / 255.0f;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::pack_argb
+//       Access: Public, Static
+//  Description: Packs four ints, stored R, G, B, A, into a
+//               packed_argb value.
+////////////////////////////////////////////////////////////////////
+unsigned int qpGeomVertexData::
+pack_argb(const int data[4]) {
+  unsigned int r = ((unsigned int)data[0]) & 0xff;
+  unsigned int g = ((unsigned int)data[1]) & 0xff;
+  unsigned int b = ((unsigned int)data[2]) & 0xff;
+  unsigned int a = ((unsigned int)data[3]) & 0xff;
+  return ((a << 24) | (r << 16) | (g << 8) | b);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::unpack_argb
+//       Access: Public, Static
+//  Description: Unpacks a packed_argb value into four ints, stored
+//               R, G, B, A.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+unpack_argb(int data[4], unsigned int packed_argb) {
+  data[0] = (int)((packed_argb >> 16) & 0xff);
+  data[1] = (int)((packed_argb >> 8) & 0xff);
+  data[2] = (int)(packed_argb & 0xff);
+  data[3] = (int)((packed_argb >> 24) & 0xff);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::do_set_num_vertices
 //       Access: Private
@@ -798,11 +1155,66 @@ do_set_num_vertices(int n, qpGeomVertexData::CDWriter &cdata) {
 
   if (any_changed) {
     cdata->_modified = qpGeom::get_next_modified();
+    cdata->_computed_vertices.clear();
   }
 
   return any_changed;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::make_computed_vertices
+//       Access: Private
+//  Description: Creates the GeomVertexData that represents the
+//               results of computing the vertex animation on the CPU.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+make_computed_vertices(qpGeomVertexData::CDWriter &cdata) {
+  // First, make a new format that doesn't have the transform_blend
+  // array.
+  cdata->_computed_vertices = replace_data_type
+    (InternalName::get_transform_blend(), 0, qpGeomVertexDataType::NT_uint16,
+     qpGeomUsageHint::UH_dynamic, false);
+
+  // Now fill it up with the appropriate data.
+  update_computed_vertices(cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::update_computed_vertices
+//       Access: Private
+//  Description: Recomputes the results of computing the vertex
+//               animation on the CPU, and applies them to the
+//               existing computed_vertices object.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+update_computed_vertices(qpGeomVertexData::CDWriter &cdata) {
+  int num_vertices = get_num_vertices();
+
+  if (gobj_cat.is_debug()) {
+    gobj_cat.debug()
+      << "Computing " << num_vertices << " vertices.\n";
+  }
+  PStatTimer timer(_compute_vertices_pcollector);
+
+  CPT(TransformBlendPalette) palette = cdata->_transform_blend_palette;
+  nassertv(palette != (TransformBlendPalette *)NULL);
+  PT(qpGeomVertexData) new_data = cdata->_computed_vertices;
+
+  // Now go through and apply the scale, copying it to the new data.
+  qpGeomVertexIterator from(this, InternalName::get_vertex());
+  qpGeomVertexIterator blendi(this, InternalName::get_transform_blend());
+  qpGeomVertexIterator to(new_data, InternalName::get_vertex());
+
+  for (int i = 0; i < num_vertices; i++) {
+    LPoint4f vertex = from.get_data4f();
+    int bi = blendi.get_data1i();
+    const TransformBlend &blend = palette->get_blend(bi);
+    blend.transform_point(vertex);
+
+    to.set_data4f(vertex);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::register_with_read_factory
 //       Access: Public, Static

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

@@ -25,6 +25,7 @@
 #include "qpgeomVertexDataType.h"
 #include "qpgeomVertexArrayData.h"
 #include "qpgeomUsageHint.h"
+#include "transformBlendPalette.h"
 #include "internalName.h"
 #include "cycleData.h"
 #include "cycleDataReader.h"
@@ -81,6 +82,11 @@ PUBLISHED:
   qpGeomVertexArrayData *modify_array(int i);
   void set_array(int i, const qpGeomVertexArrayData *array);
 
+  INLINE const TransformBlendPalette *get_transform_blend_palette() const;
+  TransformBlendPalette *modify_transform_blend_palette();
+  void set_transform_blend_palette(const TransformBlendPalette *palette);
+  INLINE void clear_transform_blend_palette();
+
   int get_num_bytes() const;
   INLINE UpdateSeq get_modified() const;
 
@@ -92,9 +98,13 @@ PUBLISHED:
     set_color(const Colorf &color, int num_components,
               qpGeomVertexDataType::NumericType numeric_type) const;
 
+  CPT(qpGeomVertexData) compute_vertices() const;
+
   PT(qpGeomVertexData) 
     replace_data_type(const InternalName *name, int num_components,
-                      qpGeomVertexDataType::NumericType numeric_type) const;
+                      qpGeomVertexDataType::NumericType numeric_type,
+                      qpGeomUsageHint::UsageHint usage_hint,
+                      bool keep_animation) const;
 
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
@@ -104,6 +114,10 @@ public:
                 int vertex, const float *data, int num_values);
   void get_data(int array, const qpGeomVertexDataType *data_type,
                 int vertex, float *data, int num_values) const;
+  void set_data(int array, const qpGeomVertexDataType *data_type,
+                int vertex, const int *data, int num_values);
+  void get_data(int array, const qpGeomVertexDataType *data_type,
+                int vertex, int *data, int num_values) const;
 
   bool get_array_info(const InternalName *name, 
                       const qpGeomVertexArrayData *&array_data,
@@ -117,6 +131,8 @@ public:
 
   static unsigned int pack_argb(const float data[4]);
   static void unpack_argb(float data[4], unsigned int packed_argb);
+  static unsigned int pack_argb(const int data[4]);
+  static void unpack_argb(int data[4], unsigned int packed_argb);
 
 private:
   CPT(qpGeomVertexFormat) _format;
@@ -135,6 +151,9 @@ private:
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     Arrays _arrays;
+    PT(TransformBlendPalette) _transform_blend_palette;
+    PT(qpGeomVertexData) _computed_vertices;
+    UpdateSeq _computed_vertices_modified;
     UpdateSeq _modified;
   };
 
@@ -144,10 +163,13 @@ private:
 
 private:
   bool do_set_num_vertices(int n, CDWriter &cdata);
+  void make_computed_vertices(CDWriter &cdata);
+  void update_computed_vertices(CDWriter &cdata);
 
   static PStatCollector _convert_pcollector;
   static PStatCollector _scale_color_pcollector;
   static PStatCollector _set_color_pcollector;
+  static PStatCollector _compute_vertices_pcollector;
 
 public:
   static void register_with_read_factory();

+ 20 - 9
panda/src/gobj/qpgeomVertexDataType.cxx

@@ -35,7 +35,11 @@ qpGeomVertexDataType(const InternalName *name, int num_components,
   nassertv(num_components > 0 && start >= 0);
 
   switch (numeric_type) {
-  case NT_uint8:
+  case NT_uint16:
+    _component_bytes = 2;  // sizeof(PN_uint16)
+    break;
+
+  case NT_ufloat8:
     _component_bytes = 1;
     break;
 
@@ -44,7 +48,7 @@ qpGeomVertexDataType(const InternalName *name, int num_components,
     _num_values *= 4;
     break;
 
-  case NT_float:
+  case NT_float32:
     _component_bytes = 4;  // sizeof(PN_float32)
     break;
   }
@@ -61,7 +65,7 @@ qpGeomVertexDataType(const InternalName *name, int num_components,
 const qpGeomVertexDataType &qpGeomVertexDataType::
 error() {
   static qpGeomVertexDataType error_result
-    (InternalName::get_error(), 1, NT_uint8, 0);
+    (InternalName::get_error(), 1, NT_ufloat8, 0);
   return error_result;
 }
 
@@ -101,10 +105,10 @@ copy_records(unsigned char *to, int to_stride,
       // An easy case.
     copy_no_convert(to, to_stride, from, from_stride, from_type, num_records);
 
-  } else if (get_numeric_type() == NT_uint8 && from_type->get_numeric_type() == NT_packed_argb &&
+  } else if (get_numeric_type() == NT_ufloat8 && from_type->get_numeric_type() == NT_packed_argb &&
              get_num_values() == from_type->get_num_values()) {
     copy_argb_to_uint8(to, to_stride, from, from_stride, from_type, num_records);
-  } else if (get_numeric_type() == NT_packed_argb && from_type->get_numeric_type() == NT_uint8 &&
+  } else if (get_numeric_type() == NT_packed_argb && from_type->get_numeric_type() == NT_ufloat8 &&
              get_num_values() == from_type->get_num_values()) {
     copy_uint8_to_argb(to, to_stride, from, from_stride, from_type, num_records);
   } else {
@@ -223,7 +227,10 @@ copy_generic(unsigned char *to, int to_stride,
 float qpGeomVertexDataType::
 get_value(const unsigned char *data, int n) const {
   switch (get_numeric_type()) {
-  case NT_uint8:
+  case NT_uint16:
+    return (float)data[n];
+
+  case NT_ufloat8:
     return (float)data[n] / 255.0f;
 
   case qpGeomVertexDataType::NT_packed_argb:
@@ -245,7 +252,7 @@ get_value(const unsigned char *data, int n) const {
     }
     break;
 
-  case qpGeomVertexDataType::NT_float:
+  case qpGeomVertexDataType::NT_float32:
     {
       const PN_float32 *float_array = (const PN_float32 *)data;
       return float_array[n];
@@ -263,7 +270,11 @@ get_value(const unsigned char *data, int n) const {
 void qpGeomVertexDataType::
 set_value(unsigned char *data, int n, float value) const {
   switch (get_numeric_type()) {
-  case NT_uint8:
+  case NT_uint16:
+    data[n] = (int)value;
+    break;
+
+  case NT_ufloat8:
     data[n] = (int)(value * 255.0f);
     break;
 
@@ -301,7 +312,7 @@ set_value(unsigned char *data, int n, float value) const {
     }
     break;
 
-  case qpGeomVertexDataType::NT_float:
+  case qpGeomVertexDataType::NT_float32:
     PN_float32 *float_array = (PN_float32 *)data;
     float_array[n] = value;
     break;

+ 4 - 3
panda/src/gobj/qpgeomVertexDataType.h

@@ -35,9 +35,10 @@
 class EXPCL_PANDA qpGeomVertexDataType {
 PUBLISHED:
   enum NumericType {
-    NT_uint8,
-    NT_packed_argb,  // DirectX style
-    NT_float,
+    NT_uint16,       // A short integer 0..65535
+    NT_ufloat8,      // A floating-point number 0..1 packed into a byte
+    NT_float32,      // A true floating-point number
+    NT_packed_argb,  // DirectX style color specification
   };
 
   INLINE qpGeomVertexDataType(const InternalName *name, int num_components,

+ 28 - 28
panda/src/gobj/qpgeomVertexFormat.cxx

@@ -526,64 +526,64 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 qpGeomVertexFormat::Registry::
 Registry() {
   _v3 = register_format(new qpGeomVertexArrayFormat
-                        (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float));
+                        (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32));
 
   _v3n3 = register_format(new qpGeomVertexArrayFormat
-                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                           InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float));
+                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                           InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32));
 
   _v3t2 = register_format(new qpGeomVertexArrayFormat
-                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                           InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float));
+                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                           InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float32));
 
   _v3n3t2 = register_format(new qpGeomVertexArrayFormat
-                            (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                             InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float,
-                             InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float));
+                            (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                             InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32,
+                             InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float32));
 
   // Define the DirectX-style packed color formats
   _v3cp = register_format(new qpGeomVertexArrayFormat
-                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
+                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
                            InternalName::get_color(), 1, qpGeomVertexDataType::NT_packed_argb));
 
   _v3n3cp = register_format(new qpGeomVertexArrayFormat
-                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                           InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float,
+                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                           InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32,
                            InternalName::get_color(), 1, qpGeomVertexDataType::NT_packed_argb));
 
   _v3cpt2 = register_format(new qpGeomVertexArrayFormat
-                            (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
+                            (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
                              InternalName::get_color(), 1, qpGeomVertexDataType::NT_packed_argb,
-                             InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float));
+                             InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float32));
 
   _v3n3cpt2 = register_format(new qpGeomVertexArrayFormat
-                              (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                               InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float,
+                              (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                               InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32,
                                InternalName::get_color(), 1, qpGeomVertexDataType::NT_packed_argb,
-                               InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float));
+                               InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float32));
 
   // Define the OpenGL-style per-byte color formats.  This is not the
   // same as a packed format, above, because the resulting byte order
   // is endian-independent.
   _v3c4 = register_format(new qpGeomVertexArrayFormat
-                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                           InternalName::get_color(), 4, qpGeomVertexDataType::NT_uint8));
+                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                           InternalName::get_color(), 4, qpGeomVertexDataType::NT_ufloat8));
 
   _v3n3c4 = register_format(new qpGeomVertexArrayFormat
-                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                           InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float,
-                           InternalName::get_color(), 4, qpGeomVertexDataType::NT_uint8));
+                          (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                           InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32,
+                           InternalName::get_color(), 4, qpGeomVertexDataType::NT_ufloat8));
 
   _v3c4t2 = register_format(new qpGeomVertexArrayFormat
-                            (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                             InternalName::get_color(), 4, qpGeomVertexDataType::NT_uint8,
-                             InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float));
+                            (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                             InternalName::get_color(), 4, qpGeomVertexDataType::NT_ufloat8,
+                             InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float32));
 
   _v3n3c4t2 = register_format(new qpGeomVertexArrayFormat
-                              (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float,
-                               InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float,
-                               InternalName::get_color(), 4, qpGeomVertexDataType::NT_uint8,
-                               InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float));
+                              (InternalName::get_vertex(), 3, qpGeomVertexDataType::NT_float32,
+                               InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32,
+                               InternalName::get_color(), 4, qpGeomVertexDataType::NT_ufloat8,
+                               InternalName::get_texcoord(), 2, qpGeomVertexDataType::NT_float32));
 }
 
 ////////////////////////////////////////////////////////////////////

+ 53 - 25
panda/src/gobj/qpgeomVertexIterator.I

@@ -291,99 +291,99 @@ get_write_vertex() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::set_data1
+//     Function: qpGeomVertexIterator::set_data1f
 //       Access: Published
 //  Description: Sets the write vertex to a particular 1-component
 //               value, and advances the write vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexIterator::
-set_data1(float data) {
+set_data1f(float data) {
   nassertv(!_const_data);
   _data->set_data(_array, _data_type, _write_vertex, &data, 1);
   ++_write_vertex;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::set_data2
+//     Function: qpGeomVertexIterator::set_data2f
 //       Access: Published
 //  Description: Sets the write vertex to a particular 2-component
 //               value, and advances the write vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexIterator::
-set_data2(float x, float y) {
-  set_data2(LVecBase2f(x, y));
+set_data2f(float x, float y) {
+  set_data2f(LVecBase2f(x, y));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::set_data2
+//     Function: qpGeomVertexIterator::set_data2f
 //       Access: Published
 //  Description: Sets the write vertex to a particular 2-component
 //               value, and advances the write vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexIterator::
-set_data2(const LVecBase2f &data) {
+set_data2f(const LVecBase2f &data) {
   nassertv(!_const_data);
   _data->set_data(_array, _data_type, _write_vertex, data.get_data(), 2);
   ++_write_vertex;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::set_data3
+//     Function: qpGeomVertexIterator::set_data3f
 //       Access: Published
 //  Description: Sets the write vertex to a particular 3-component
 //               value, and advances the write vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexIterator::
-set_data3(float x, float y, float z) {
-  set_data3(LVecBase3f(x, y, z));
+set_data3f(float x, float y, float z) {
+  set_data3f(LVecBase3f(x, y, z));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::set_data3
+//     Function: qpGeomVertexIterator::set_data3f
 //       Access: Published
 //  Description: Sets the write vertex to a particular 3-component
 //               value, and advances the write vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexIterator::
-set_data3(const LVecBase3f &data) {
+set_data3f(const LVecBase3f &data) {
   nassertv(!_const_data);
   _data->set_data(_array, _data_type, _write_vertex, data.get_data(), 3);
   ++_write_vertex;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::set_data4
+//     Function: qpGeomVertexIterator::set_data4f
 //       Access: Published
 //  Description: Sets the write vertex to a particular 4-component
 //               value, and advances the write vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexIterator::
-set_data4(float x, float y, float z, float w) {
-  set_data4(LVecBase4f(x, y, z, w));
+set_data4f(float x, float y, float z, float w) {
+  set_data4f(LVecBase4f(x, y, z, w));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::set_data4
+//     Function: qpGeomVertexIterator::set_data4f
 //       Access: Published
 //  Description: Sets the write vertex to a particular 4-component
 //               value, and advances the write vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexIterator::
-set_data4(const LVecBase4f &data) {
+set_data4f(const LVecBase4f &data) {
   nassertv(!_const_data);
   _data->set_data(_array, _data_type, _write_vertex, data.get_data(), 4);
   ++_write_vertex;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::get_data1
+//     Function: qpGeomVertexIterator::get_data1f
 //       Access: Published
 //  Description: Returns the data associated with the read vertex,
 //               expressed as a 1-component value, and advances the
 //               read vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE float qpGeomVertexIterator::
-get_data1() {
+get_data1f() {
   float data;
   _data->get_data(_array, _data_type, _read_vertex, &data, 1);
   ++_read_vertex;
@@ -391,14 +391,14 @@ get_data1() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::get_data2
+//     Function: qpGeomVertexIterator::get_data2f
 //       Access: Published
 //  Description: Returns the data associated with the read vertex,
 //               expressed as a 2-component value, and advances the
 //               read vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE LVecBase2f qpGeomVertexIterator::
-get_data2() {
+get_data2f() {
   float data[4];
   int num_values = min(_data_type->get_num_values(), 4);
   _data->get_data(_array, _data_type, _read_vertex, data, num_values);
@@ -409,14 +409,14 @@ get_data2() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::get_data3
+//     Function: qpGeomVertexIterator::get_data3f
 //       Access: Published
 //  Description: Returns the data associated with the read vertex,
 //               expressed as a 3-component value, and advances the
 //               read vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE LVecBase3f qpGeomVertexIterator::
-get_data3() {
+get_data3f() {
   float data[4];
   int num_values = min(_data_type->get_num_values(), 4);
   _data->get_data(_array, _data_type, _read_vertex, data, num_values);
@@ -427,14 +427,14 @@ get_data3() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexIterator::get_data4
+//     Function: qpGeomVertexIterator::get_data4f
 //       Access: Published
 //  Description: Returns the data associated with the read vertex,
 //               expressed as a 4-component value, and advances the
 //               read vertex.
 ////////////////////////////////////////////////////////////////////
 INLINE LVecBase4f qpGeomVertexIterator::
-get_data4() {
+get_data4f() {
   float data[4];
   int num_values = min(_data_type->get_num_values(), 4);
   _data->get_data(_array, _data_type, _read_vertex, data, num_values);
@@ -443,3 +443,31 @@ get_data4() {
   qpGeomVertexData::to_vec4(result, data, num_values);
   return result;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexIterator::set_data1i
+//       Access: Published
+//  Description: Sets the write vertex to a particular 1-component
+//               value, and advances the write vertex.
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomVertexIterator::
+set_data1i(int data) {
+  nassertv(!_const_data);
+  _data->set_data(_array, _data_type, _write_vertex, &data, 1);
+  ++_write_vertex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexIterator::get_data1i
+//       Access: Published
+//  Description: Returns the data associated with the read vertex,
+//               expressed as a 1-component value, and advances the
+//               read vertex.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexIterator::
+get_data1i() {
+  int data;
+  _data->get_data(_array, _data_type, _read_vertex, &data, 1);
+  ++_read_vertex;
+  return data;
+}

+ 18 - 11
panda/src/gobj/qpgeomVertexIterator.h

@@ -41,12 +41,16 @@ PUBLISHED:
                               const string &name);
   INLINE qpGeomVertexIterator(qpGeomVertexData *data,
                               const InternalName *name);
+public:
+  // These const variants are not published, since our Python layer
+  // doesn't know the difference between const and non-const anyway.
   INLINE qpGeomVertexIterator(const qpGeomVertexData *data);
   INLINE qpGeomVertexIterator(const qpGeomVertexData *data,
                               const string &name);
   INLINE qpGeomVertexIterator(const qpGeomVertexData *data,
                               const InternalName *name);
 
+PUBLISHED:
   INLINE const qpGeomVertexData *get_data() const;
 
   INLINE void set_data_type(int data_type);
@@ -63,18 +67,21 @@ PUBLISHED:
   INLINE int get_read_vertex() const;
   INLINE int get_write_vertex() const;
 
-  INLINE void set_data1(float data);
-  INLINE void set_data2(float x, float y);
-  INLINE void set_data2(const LVecBase2f &data);
-  INLINE void set_data3(float x, float y, float z);
-  INLINE void set_data3(const LVecBase3f &data);
-  INLINE void set_data4(float x, float y, float z, float w);
-  INLINE void set_data4(const LVecBase4f &data);
+  INLINE void set_data1f(float data);
+  INLINE void set_data2f(float x, float y);
+  INLINE void set_data2f(const LVecBase2f &data);
+  INLINE void set_data3f(float x, float y, float z);
+  INLINE void set_data3f(const LVecBase3f &data);
+  INLINE void set_data4f(float x, float y, float z, float w);
+  INLINE void set_data4f(const LVecBase4f &data);
+
+  INLINE float get_data1f();
+  INLINE LVecBase2f get_data2f();
+  INLINE LVecBase3f get_data3f();
+  INLINE LVecBase4f get_data4f();
 
-  INLINE float get_data1();
-  INLINE LVecBase2f get_data2();
-  INLINE LVecBase3f get_data3();
-  INLINE LVecBase4f get_data4();
+  INLINE void set_data1i(int data);
+  INLINE int get_data1i();
 
 private:
   PT(qpGeomVertexData) _data;

+ 277 - 0
panda/src/gobj/transformBlend.I

@@ -0,0 +1,277 @@
+// Filename: transformBlend.I
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: TransformBlend::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::
+TransformBlend() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::
+TransformBlend(const VertexTransform *transform0, float) {
+  add_transform(transform0, 1.0f);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::
+TransformBlend(const VertexTransform *transform0, float weight0,
+               const VertexTransform *transform1, float weight1) {
+  add_transform(transform0, weight0);
+  add_transform(transform1, weight1);
+  normalize_weights();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::
+TransformBlend(const VertexTransform *transform0, float weight0,
+               const VertexTransform *transform1, float weight1,
+               const VertexTransform *transform2, float weight2) {
+  add_transform(transform0, weight0);
+  add_transform(transform1, weight1);
+  add_transform(transform2, weight2);
+  normalize_weights();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::
+TransformBlend(const VertexTransform *transform0, float weight0,
+               const VertexTransform *transform1, float weight1,
+               const VertexTransform *transform2, float weight2,
+               const VertexTransform *transform3, float weight3) {
+  add_transform(transform0, weight0);
+  add_transform(transform1, weight1);
+  add_transform(transform2, weight2);
+  add_transform(transform3, weight3);
+  normalize_weights();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::
+TransformBlend(const TransformBlend &copy) :
+  _entries(copy._entries)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void TransformBlend::
+operator = (const TransformBlend &copy) {
+  _entries = copy._entries;
+  clear_result();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::
+~TransformBlend() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::operator <
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformBlend::
+operator < (const TransformBlend &other) const {
+  return compare_to(other) < 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::operator ==
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformBlend::
+operator == (const TransformBlend &other) const {
+  return compare_to(other) == 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::operator !=
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformBlend::
+operator != (const TransformBlend &other) const {
+  return compare_to(other) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::get_num_transforms
+//       Access: Published
+//  Description: Returns the number of transforms stored in the blend
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE int TransformBlend::
+get_num_transforms() const {
+  return _entries.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::get_transform
+//       Access: Published
+//  Description: Returns the nth transform stored in the blend
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE const VertexTransform *TransformBlend::
+get_transform(int n) const {
+  nassertr(n >= 0 && n < (int)_entries.size(), NULL);
+  return _entries[n]._transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::get_weight
+//       Access: Published
+//  Description: Returns the weight associated with the nth transform
+//               stored in the blend object.
+////////////////////////////////////////////////////////////////////
+INLINE float TransformBlend::
+get_weight(int n) const {
+  nassertr(n >= 0 && n < (int)_entries.size(), 0.0f);
+  return _entries[n]._weight;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::get_blend
+//       Access: Published
+//  Description: Returns the current value of the blend, based on the
+//               current value of all of the nested transform objects
+//               and their associated weights.
+////////////////////////////////////////////////////////////////////
+INLINE void TransformBlend::
+get_blend(LMatrix4f &result) const {
+  CDReader cdata(_cycler);
+  if (cdata->_global_modified != VertexTransform::get_global_modified()) {
+    CDWriter cdataw(((TransformBlend *)this)->_cycler, cdata);
+    ((TransformBlend *)this)->recompute_result(cdataw);
+    result = cdataw->_result;
+  } else {
+    result = cdata->_result;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::transform_point
+//       Access: Published
+//  Description: Transforms the indicated point by the blend matrix.
+////////////////////////////////////////////////////////////////////
+INLINE void TransformBlend::
+transform_point(LPoint4f &point) const {
+  if (!_entries.empty()) {
+    CDReader cdata(_cycler);
+    if (cdata->_global_modified != VertexTransform::get_global_modified()) {
+      CDWriter cdataw(((TransformBlend *)this)->_cycler, cdata);
+      ((TransformBlend *)this)->recompute_result(cdataw);
+      point = point * cdataw->_result;
+    } else {
+      point = point * cdata->_result;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::get_modified
+//       Access: Published
+//  Description: Returns a counter which is guaranteed to increment at
+//               least as often as the result of get_blend() changes.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq TransformBlend::
+get_modified() const {
+  CDReader cdata(_cycler);
+  if (cdata->_global_modified != VertexTransform::get_global_modified()) {
+    CDWriter cdataw(((TransformBlend *)this)->_cycler, cdata);
+    ((TransformBlend *)this)->recompute_result(cdataw);
+    return cdataw->_modified;
+  } else {
+    return cdata->_modified;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::TransformEntry::operator <
+//       Access: Public
+//  Description: Provides an ordering of TransformEntries by the
+//               VertexTransform pointer only, so we can easily look
+//               up in the set to see if a particular transform
+//               exists.
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformBlend::TransformEntry::
+operator < (const TransformBlend::TransformEntry &other) const {
+  return _transform < other._transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::CData::
+CData() :
+  _result(LMatrix4f::ident_mat())
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlend::CData::
+CData(const TransformBlend::CData &copy) :
+  _result(copy._result),
+  _modified(copy._modified),
+  _global_modified(copy._global_modified)
+{
+}
+
+INLINE ostream &
+operator << (ostream &out, const TransformBlend &obj) {
+  obj.output(out);
+  return out;
+}

+ 287 - 0
panda/src/gobj/transformBlend.cxx

@@ -0,0 +1,287 @@
+// Filename: transformBlend.cxx
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "transformBlend.h"
+#include "indent.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::compare_to
+//       Access: Published
+//  Description: Defines an arbitrary ordering for TransformBlend
+//               objects.
+////////////////////////////////////////////////////////////////////
+int TransformBlend::
+compare_to(const TransformBlend &other) const {
+  if (_entries.size() != other._entries.size()) {
+    return (int)_entries.size() - (int)other._entries.size();
+  }
+
+  Entries::const_iterator ai, bi;
+  ai = _entries.begin();
+  bi = other._entries.begin();
+  while (ai != _entries.end() && bi != other._entries.end()) {
+    if ((*ai)._transform != (*bi)._transform) {
+      return (*ai)._transform < (*bi)._transform ? -1 : 1;
+    }
+    if (!IS_NEARLY_EQUAL((*ai)._weight, (*bi)._weight)) {
+      return (*ai)._weight < (*bi)._weight ? -1 : 1;
+    }
+    ++ai;
+    ++bi;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::add_transform
+//       Access: Published
+//  Description: Adds a new transform to the blend.  If the transform
+//               already existed, increases its weight factor.
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+add_transform(const VertexTransform *transform, float weight) {
+  if (!IS_NEARLY_ZERO(weight)) {
+    TransformEntry entry;
+    entry._transform = transform;
+    entry._weight = weight;
+    pair<Entries::iterator, bool> result = _entries.insert(entry);
+    if (!result.second) {
+      // If the new value was not inserted, it was already there;
+      // increment the existing weight factor.
+      Entries::iterator ei = result.first;
+      (*ei)._weight += weight;
+      if (IS_NEARLY_ZERO((*ei)._weight)) {
+        // If we have just zeroed out the weight, remove it.
+        _entries.erase(ei);
+      }
+    }
+    clear_result();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::remove_transform
+//       Access: Published
+//  Description: Removes the indicated transform to the blend.
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+remove_transform(const VertexTransform *transform) {
+  TransformEntry entry;
+  entry._transform = transform;
+  entry._weight = 0.0f;
+  Entries::iterator ei = _entries.find(entry);
+  if (ei != _entries.end()) {
+    _entries.erase(ei);
+  }
+  clear_result();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::normalize_weights
+//       Access: Published
+//  Description: Rescales all of the weights on the various transforms
+//               so that they sum to 1.0.  It is generally a good idea
+//               to call this after adding or removing transforms from
+//               the blend.
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+normalize_weights() {
+  float net_weight = 0.0f;
+  Entries::iterator ei;
+  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
+    net_weight += (*ei)._weight;
+  }
+  if (net_weight != 0.0f) {
+    for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
+      (*ei)._weight /= net_weight;
+    }
+  }
+  clear_result();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::has_transform
+//       Access: Published
+//  Description: Returns true if the blend has the indicated
+//               transform, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool TransformBlend::
+has_transform(const VertexTransform *transform) const {
+  TransformEntry entry;
+  entry._transform = transform;
+  entry._weight = 0.0f;
+  Entries::const_iterator ei = _entries.find(entry);
+  return (ei != _entries.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::get_weight
+//       Access: Published
+//  Description: Returns the weight associated with the indicated
+//               transform, or 0 if there is no entry for the
+//               transform.
+////////////////////////////////////////////////////////////////////
+float TransformBlend::
+get_weight(const VertexTransform *transform) const {
+  TransformEntry entry;
+  entry._transform = transform;
+  entry._weight = 0.0f;
+  Entries::const_iterator ei = _entries.find(entry);
+  if (ei != _entries.end()) {
+    return (*ei)._weight;
+  }
+  return 0.0f;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+output(ostream &out) const {
+  if (_entries.empty()) {
+    out << "empty";
+  } else {
+    Entries::const_iterator ei = _entries.begin();
+    out << *(*ei)._transform << ":" << (*ei)._weight;
+    ++ei;
+    while (ei != _entries.end()) {
+      out << " " << *(*ei)._transform << ":" << (*ei)._weight;
+      ++ei;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+write(ostream &out, int indent_level) const {
+  Entries::const_iterator ei;
+  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
+    indent(out, indent_level)
+      << *(*ei)._transform << ":" << (*ei)._weight << "\n";
+    LMatrix4f mat;
+    (*ei)._transform->get_matrix(mat);
+    mat.write(out, indent_level + 4);
+  }
+  LMatrix4f blend;
+  get_blend(blend);
+  indent(out, indent_level)
+    << "Blended result =\n";
+  blend.write(out, indent_level + 2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::recompute_result
+//       Access: Private
+//  Description: Recomputes the blend result from the various
+//               VertexTransform objects, if necessary.
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+recompute_result(TransformBlend::CDWriter &cdata) {
+  // Update the global_modified sequence number first, to prevent race
+  // conditions.
+  cdata->_global_modified = VertexTransform::get_global_modified();
+
+  // Now see if we really need to recompute.
+  UpdateSeq seq;
+  Entries::const_iterator ei;
+  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
+    seq = max(seq, (*ei)._transform->get_modified());
+  }
+
+  if (cdata->_modified != seq) {
+    // We do need to recompute.
+    cdata->_modified = seq;
+
+    cdata->_result.set(0.0f, 0.0f, 0.0f, 0.0f,
+                       0.0f, 0.0f, 0.0f, 0.0f,
+                       0.0f, 0.0f, 0.0f, 0.0f,
+                       0.0f, 0.0f, 0.0f, 0.0f);
+    for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
+      (*ei)._transform->accumulate_matrix(cdata->_result, (*ei)._weight);
+    }
+  }    
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::clear_result
+//       Access: Private
+//  Description: Removes the computed result to force it to be
+//               recomputed.
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+clear_result() {
+  CDWriter cdata(_cycler);
+  cdata->_global_modified = UpdateSeq();
+  if (cdata->_modified != UpdateSeq()) {
+    cdata->_modified = UpdateSeq();
+    cdata->_result = LMatrix4f::ident_mat();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::write_datagram
+//       Access: Public
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::complete_pointers
+//       Access: Public
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int TransformBlend::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::fillin
+//       Access: Public
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new PandaNode.
+////////////////////////////////////////////////////////////////////
+void TransformBlend::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlend::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *TransformBlend::CData::
+make_copy() const {
+  return new CData(*this);
+}

+ 123 - 0
panda/src/gobj/transformBlend.h

@@ -0,0 +1,123 @@
+// Filename: transformBlend.h
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 TRANSFORMBLEND_H
+#define TRANSFORMBLEND_H
+
+#include "pandabase.h"
+#include "vertexTransform.h"
+#include "pointerTo.h"
+#include "pvector.h"
+#include "ordered_vector.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TransformBlend
+// Description : This defines a single entry in a
+//               TransformBlendPalette.  It represents a unique
+//               combination of VertexTransform pointers and blend
+//               amounts.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA TransformBlend {
+PUBLISHED:
+  INLINE TransformBlend();
+  INLINE TransformBlend(const VertexTransform *transform0, float weight0);
+  INLINE TransformBlend(const VertexTransform *transform0, float weight0,
+                        const VertexTransform *transform1, float weight1);
+  INLINE TransformBlend(const VertexTransform *transform0, float weight0,
+                        const VertexTransform *transform1, float weight1,
+                        const VertexTransform *transform2, float weight2);
+  INLINE TransformBlend(const VertexTransform *transform0, float weight0,
+                        const VertexTransform *transform1, float weight1,
+                        const VertexTransform *transform2, float weight2,
+                        const VertexTransform *transform3, float weight3);
+  INLINE TransformBlend(const TransformBlend &copy);
+  INLINE void operator = (const TransformBlend &copy);
+  INLINE ~TransformBlend();
+
+  int compare_to(const TransformBlend &other) const;
+  INLINE bool operator < (const TransformBlend &other) const;
+  INLINE bool operator == (const TransformBlend &other) const;
+  INLINE bool operator != (const TransformBlend &other) const;
+
+  void add_transform(const VertexTransform *transform, float weight);
+  void remove_transform(const VertexTransform *transform);
+  void normalize_weights();
+  bool has_transform(const VertexTransform *transform) const;
+  float get_weight(const VertexTransform *transform) const;
+
+  INLINE int get_num_transforms() const;
+  INLINE const VertexTransform *get_transform(int n) const;
+  INLINE float get_weight(int n) const;
+
+  INLINE void get_blend(LMatrix4f &result) const;
+  INLINE void transform_point(LPoint4f &point) const;
+  INLINE UpdateSeq get_modified() const;
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level) const;
+
+private:
+  class TransformEntry {
+  public:
+    INLINE bool operator < (const TransformEntry &other) const;
+
+    CPT(VertexTransform) _transform;
+    float _weight;
+  };
+  typedef ov_set<TransformEntry> Entries;
+  Entries _entries;
+
+  // This is the data that must be cycled between pipeline stages; it
+  // is just a local cache.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+
+    LMatrix4f _result;
+    UpdateSeq _modified;
+    UpdateSeq _global_modified;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+  void recompute_result(CDWriter &cdata);
+  void clear_result();
+
+public:
+  void write_datagram(BamWriter *manager, Datagram &dg) const;
+  int complete_pointers(TypedWritable **plist, BamReader *manager);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+  friend class VertexTransform;
+};
+
+INLINE ostream &operator << (ostream &out, const TransformBlend &obj);
+
+#include "transformBlend.I"
+
+#endif

+ 80 - 0
panda/src/gobj/transformBlendPalette.I

@@ -0,0 +1,80 @@
+// Filename: transformBlendPalette.I
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: TransformBlendPalette::get_num_blends
+//       Access: Published
+//  Description: Returns the total number of different blend
+//               combinations in the palette.
+////////////////////////////////////////////////////////////////////
+INLINE int TransformBlendPalette::
+get_num_blends() const {
+  return _blends.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::get_blend
+//       Access: Published
+//  Description: Returns the nth blend in the palette.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformBlend &TransformBlendPalette::
+get_blend(int n) const {
+  nassertr(n >= 0 && n < (int)_blends.size(), _blends[0]);
+  return _blends[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::get_modified
+//       Access: Published
+//  Description: Returns a counter which is guaranteed to increment at
+//               least when any TransformBlends within the palette
+//               have changed.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq TransformBlendPalette::
+get_modified() const {
+  CDReader cdata(_cycler);
+  if (cdata->_global_modified != VertexTransform::get_global_modified()) {
+    CDWriter cdataw(((TransformBlendPalette *)this)->_cycler, cdata);
+    ((TransformBlendPalette *)this)->recompute_modified(cdataw);
+    return cdataw->_modified;
+  } else {
+    return cdata->_modified;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlendPalette::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TransformBlendPalette::CData::
+CData(const TransformBlendPalette::CData &copy) :
+  _modified(copy._modified),
+  _global_modified(copy._global_modified)
+{
+}

+ 269 - 0
panda/src/gobj/transformBlendPalette.cxx

@@ -0,0 +1,269 @@
+// Filename: transformBlendPalette.cxx
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "transformBlendPalette.h"
+#include "indent.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle TransformBlendPalette::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TransformBlendPalette::
+TransformBlendPalette() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TransformBlendPalette::
+TransformBlendPalette(const TransformBlendPalette &copy) :
+  _blends(copy._blends)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+operator = (const TransformBlendPalette &copy) {
+  _blends = copy._blends;
+  clear_index();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TransformBlendPalette::
+~TransformBlendPalette() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::set_blend
+//       Access: Published
+//  Description: Replaces the blend at the nth position with the
+//               indicated value.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+set_blend(int n, const TransformBlend &blend) {
+  nassertv(n >= 0 && n < (int)_blends.size());
+  _blends[n] = blend;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::remove_blend
+//       Access: Published
+//  Description: Removes the blend at the nth position.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+remove_blend(int n) {
+  nassertv(n >= 0 && n < (int)_blends.size());
+  _blends.erase(_blends.begin() + n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::add_blend
+//       Access: Published
+//  Description: Adds a new blend to the palette, and returns its
+//               index number.  If there is already an identical blend
+//               in the palette, simply returns that number instead.
+////////////////////////////////////////////////////////////////////
+int TransformBlendPalette::
+add_blend(const TransformBlend &blend) {
+  if (_blend_index.empty()) {
+    rebuild_index();
+  }
+
+  BlendIndex::iterator bi;
+  bi = _blend_index.find(&blend);
+  if (bi != _blend_index.end()) {
+    // Already had it.
+    return (*bi).second;
+  }
+
+  int new_position = (int)_blends.size();
+  _blends.push_back(blend);
+  clear_index();
+
+  return new_position;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+write(ostream &out) const {
+  for (int i = 0; i < (int)_blends.size(); i++) {
+    out << i << ". " << _blends[i] << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::clear_index
+//       Access: Private
+//  Description: Resets the index so that it will be rebuilt next time
+//               it is needed.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+clear_index() {
+  _blend_index.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::rebuild_index
+//       Access: Private
+//  Description: Rebuilds the index so that we can easily determine
+//               what blend combinations are already present in the
+//               palette.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+rebuild_index() {
+  _blend_index.clear();
+
+  for (int i = 0; i < (int)_blends.size(); i++) {
+    _blend_index[&_blends[i]] = i;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::recompute_modified
+//       Access: Private
+//  Description: Recomputes the modified stamp from the various
+//               TransformBlend objects, if necessary.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+recompute_modified(TransformBlendPalette::CDWriter &cdata) {
+  // Update the global_modified sequence number first, to prevent race
+  // conditions.
+  cdata->_global_modified = VertexTransform::get_global_modified();
+
+  // Now get the local modified number.
+  UpdateSeq seq;
+  Blends::const_iterator bi;
+  for (bi = _blends.begin(); bi != _blends.end(); ++bi) {
+    seq = max(seq, (*bi).get_modified());
+  }
+
+  cdata->_modified = seq;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::clear_modified
+//       Access: Private
+//  Description: Clears the modified stamp to force it to be
+//               recomputed.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+clear_modified() {
+  CDWriter cdata(_cycler);
+  cdata->_global_modified = UpdateSeq();
+  cdata->_modified = UpdateSeq();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               TransformBlendPalette.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::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 TransformBlendPalette::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritable::complete_pointers(p_list, manager);
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type TransformBlendPalette is encountered
+//               in the Bam file.  It should create the TransformBlendPalette
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *TransformBlendPalette::
+make_from_bam(const FactoryParams &params) {
+  TransformBlendPalette *object = new TransformBlendPalette;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::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 TransformBlendPalette.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *TransformBlendPalette::CData::
+make_copy() const {
+  return new CData(*this);
+}

+ 141 - 0
panda/src/gobj/transformBlendPalette.h

@@ -0,0 +1,141 @@
+// Filename: transformBlendPalette.h
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 TRANSFORMBLENDPALETTE_H
+#define TRANSFORMBLENDPALETTE_H
+
+#include "pandabase.h"
+#include "transformBlend.h"
+#include "vertexTransform.h"
+#include "typedWritableReferenceCount.h"
+#include "pointerTo.h"
+#include "pvector.h"
+#include "pmap.h"
+#include "indirectLess.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TransformBlendPalette
+// Description : This structure collects together the different
+//               combinations of transforms and blend amounts used by
+//               a GeomVertexData, to facilitate computing dynamic
+//               vertices on the CPU at runtime.  Each vertex has a
+//               pointer to exactly one of entries in this palette,
+//               and each entry defines a number of transform/blend
+//               combinations.
+//
+//               This structure is used for a GeomVertexData set up to
+//               compute its dynamic vertices on the CPU.  See
+//               TransformPalette for one set up to compute its
+//               dynamic vertices on the graphics card.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA TransformBlendPalette : public TypedWritableReferenceCount {
+PUBLISHED:
+  TransformBlendPalette();
+  TransformBlendPalette(const TransformBlendPalette &copy);
+  void operator = (const TransformBlendPalette &copy);
+  virtual ~TransformBlendPalette();
+
+  INLINE int get_num_blends() const;
+  INLINE const TransformBlend &get_blend(int n) const;
+  INLINE UpdateSeq get_modified() const;
+
+  void set_blend(int n, const TransformBlend &blend);
+  void remove_blend(int n);
+  int add_blend(const TransformBlend &blend);
+
+  void write(ostream &out) const;
+
+private:
+  void clear_index();
+  void rebuild_index();
+
+private:
+  // We don't bother with registering the palette, or protecting its
+  // data in a CycleData structure--the interface on GeomVertexData
+  // guarantees that the pointer will be copied if we modify the
+  // palette.
+  typedef pvector<TransformBlend> Blends;
+  Blends _blends;
+
+  // 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,
+  // invalidating all the pointers into it).
+  typedef pmap<const TransformBlend *, int, IndirectLess<TransformBlend> > BlendIndex;
+  BlendIndex _blend_index;
+
+  // Even though we don't store the actual blend palette data in a
+  // CycleData structure, we do need to keep a local cache of the
+  // relevant modified stamps there, so it can be updated per-thread.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+
+    UpdateSeq _modified;
+    UpdateSeq _global_modified;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+  void recompute_modified(CDWriter &cdata);
+  void clear_modified();
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+
+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, "TransformBlendPalette",
+                  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 VertexTransform;
+};
+
+INLINE ostream &operator << (ostream &out, const TransformBlendPalette &obj);
+
+#include "transformBlendPalette.I"
+
+#endif

+ 131 - 0
panda/src/gobj/transformPalette.I

@@ -0,0 +1,131 @@
+// Filename: transformPalette.I
+// Created by:  drose (23Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: TransformPalette::is_registered
+//       Access: Published
+//  Description: Returns true if this palette has been registered.
+//               Once it has been registered, the set of transforms in
+//               a TransformPalette may not be further modified; but
+//               it must be registered before it can be assigned to a
+//               Geom.
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformPalette::
+is_registered() const {
+  return _is_registered;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::register_palette
+//       Access: Published, Static
+//  Description: Registers a TransformPalette for use.  This is
+//               similar to GeomVertexFormat::register_format().  Once
+//               registered, a TransformPalette may no longer be
+//               modified (although the individual VertexTransform
+//               objects may modify their reported transforms).
+//
+//               This must be called before a palette 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(TransformPalette) TransformPalette::
+register_palette(TransformPalette *palette) {
+  // We don't actually bother adding the palette object to a registry.
+  // This means there may be multiple copies of identical registered
+  // TransformPalettes.  Big deal.  We can always go back and make a
+  // registry later if we really need it.
+  if (palette->is_registered()) {
+    return palette;
+  }
+
+  palette->do_register();
+  return palette;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::get_num_transforms
+//       Access: Published
+//  Description: Returns the number of transforms in the palette.
+////////////////////////////////////////////////////////////////////
+INLINE int TransformPalette::
+get_num_transforms() const {
+  return _transforms.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::get_transform
+//       Access: Published
+//  Description: Returns the nth transform in the palette.
+////////////////////////////////////////////////////////////////////
+INLINE const VertexTransform *TransformPalette::
+get_transform(int n) const {
+  nassertr(n >= 0 && n < (int)_transforms.size(), NULL);
+  return _transforms[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::get_modified
+//       Access: Published
+//  Description: Returns a sequence number that's guaranteed to change
+//               at least when any VertexTransforms in the palette
+//               change.  (However, this is only true for a registered
+//               palette.  An unregistered palette may or may not
+//               reflect an update here when a VertexTransform
+//               changes.)
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq TransformPalette::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::update_modified
+//       Access: Private
+//  Description: Called internally whenever a nested VertexTransform
+//               reports that it has been modified.
+////////////////////////////////////////////////////////////////////
+INLINE void TransformPalette::
+update_modified(UpdateSeq) {
+  CDWriter cdata(_cycler);
+  ++(cdata->_modified);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TransformPalette::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TransformPalette::CData::
+CData(const TransformPalette::CData &copy) :
+  _modified(copy._modified)
+{
+}

+ 259 - 0
panda/src/gobj/transformPalette.cxx

@@ -0,0 +1,259 @@
+// Filename: transformPalette.cxx
+// Created by:  drose (23Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "transformPalette.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle TransformPalette::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TransformPalette::
+TransformPalette() :
+  _is_registered(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TransformPalette::
+TransformPalette(const TransformPalette &copy) :
+  _is_registered(false),
+  _transforms(copy._transforms)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+operator = (const TransformPalette &copy) {
+  nassertv(!_is_registered);
+  _transforms = copy._transforms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TransformPalette::
+~TransformPalette() {
+  if (_is_registered) {
+    do_unregister();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::set_transform
+//       Access: Published
+//  Description: Replaces the nth transform.  Only valid for
+//               unregistered palettes.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+set_transform(int n, VertexTransform *transform) {
+  nassertv(!_is_registered);
+  nassertv(n >= 0 && n < (int)_transforms.size());
+  _transforms[n] = transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::remove_transform
+//       Access: Published
+//  Description: Removes the nth transform.  Only valid for
+//               unregistered palettes.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+remove_transform(int n) {
+  nassertv(!_is_registered);
+  nassertv(n >= 0 && n < (int)_transforms.size());
+  _transforms.erase(_transforms.begin() + n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::add_transform
+//       Access: Published
+//  Description: Adds a new transform to the palette and returns the
+//               index number of the new transform.  Only valid for
+//               unregistered palettes.
+////////////////////////////////////////////////////////////////////
+int TransformPalette::
+add_transform(VertexTransform *transform) {
+  nassertr(!_is_registered, -1);
+  int new_index = (int)_transforms.size();
+  _transforms.push_back(transform);
+  return new_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+write(ostream &out) const {
+  for (size_t i = 0; i < _transforms.size(); ++i) {
+    out << i << ". " << *_transforms[i] << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::do_register
+//       Access: Private
+//  Description: Called internally when the palette is registered.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+do_register() {
+  nassertv(!_is_registered);
+
+  Transforms::iterator ti;
+  for (ti = _transforms.begin(); ti != _transforms.end(); ++ti) {
+    bool inserted = (*ti)->_palettes.insert(this).second;
+    nassertv(inserted);
+  }
+  _is_registered = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::do_unregister
+//       Access: Private
+//  Description: Called internally when the palette is unregistered
+//               (i.e. right before destruction).
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+do_unregister() {
+  nassertv(_is_registered);
+
+  Transforms::iterator ti;
+  for (ti = _transforms.begin(); ti != _transforms.end(); ++ti) {
+    (*ti)->_palettes.erase(this);
+  }
+  _is_registered = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               TransformPalette.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type TransformPalette is encountered
+//               in the Bam file.  It should create the TransformPalette
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *TransformPalette::
+make_from_bam(const FactoryParams &params) {
+  TransformPalette *object = new TransformPalette;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::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 TransformPalette.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *TransformPalette::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::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 TransformPalette::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::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 TransformPalette.
+////////////////////////////////////////////////////////////////////
+void TransformPalette::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 127 - 0
panda/src/gobj/transformPalette.h

@@ -0,0 +1,127 @@
+// Filename: transformPalette.h
+// Created by:  drose (23Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 TRANSFORMPALETTE_H
+#define TRANSFORMPALETTE_H
+
+#include "pandabase.h"
+#include "vertexTransform.h"
+#include "typedWritableReferenceCount.h"
+#include "pointerTo.h"
+#include "luse.h"
+#include "pvector.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : TransformPalette
+// Description : Stores the total set of VertexTransforms that the
+//               vertices in a particular GeomVertexData object might
+//               depend on.
+//
+//               This structure is used for a GeomVertexData set up to
+//               compute its dynamic vertices on the graphics card.
+//               See TransformBlendPalette for one set up to compute
+//               its dynamic vertices on the CPU.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA TransformPalette : public TypedWritableReferenceCount {
+PUBLISHED:
+  TransformPalette();
+  TransformPalette(const TransformPalette &copy);
+  void operator = (const TransformPalette &copy);
+  virtual ~TransformPalette();
+
+  INLINE bool is_registered() const;
+  INLINE static CPT(TransformPalette) register_palette(TransformPalette *palette);
+
+  INLINE int get_num_transforms() const;
+  INLINE const VertexTransform *get_transform(int n) const;
+  INLINE UpdateSeq get_modified() const;
+
+  void set_transform(int n, VertexTransform *transform);
+  void remove_transform(int n);
+  int add_transform(VertexTransform *transform);
+
+  void write(ostream &out) const;
+
+private:
+  void do_register();
+  void do_unregister();
+  INLINE void update_modified(UpdateSeq modified);
+
+private:
+  bool _is_registered;
+
+  typedef pvector< PT(VertexTransform) > Transforms;
+  Transforms _transforms;
+
+  // 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, "TransformPalette",
+                  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 VertexTransform;
+};
+
+INLINE ostream &operator << (ostream &out, const TransformPalette &obj);
+
+#include "transformPalette.I"
+
+#endif

+ 63 - 0
panda/src/gobj/userVertexTransform.I

@@ -0,0 +1,63 @@
+// Filename: userVertexTransform.I
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: UserVertexTransform::get_name
+//       Access: Published
+//  Description: Returns the name passed to the constructor.
+//               Completely arbitrary.
+////////////////////////////////////////////////////////////////////
+INLINE const string &UserVertexTransform::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::set_matrix
+//       Access: Published
+//  Description: Stores the indicated matrix.
+////////////////////////////////////////////////////////////////////
+INLINE void UserVertexTransform::
+set_matrix(const LMatrix4f &matrix) {
+  CDWriter cdata(_cycler);
+  cdata->_matrix = matrix;
+  mark_modified();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE UserVertexTransform::CData::
+CData() :
+  _matrix(LMatrix4f::ident_mat())
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE UserVertexTransform::CData::
+CData(const UserVertexTransform::CData &copy) :
+  _matrix(copy._matrix)
+{
+}

+ 144 - 0
panda/src/gobj/userVertexTransform.cxx

@@ -0,0 +1,144 @@
+// Filename: userVertexTransform.cxx
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "userVertexTransform.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle UserVertexTransform::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+UserVertexTransform::
+UserVertexTransform(const string &name) :
+  _name(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::get_matrix
+//       Access: Published, Virtual
+//  Description: Stores the transform's matrix in the indicated value.
+////////////////////////////////////////////////////////////////////
+void UserVertexTransform::
+get_matrix(LMatrix4f &matrix) const {
+  CDReader cdata(_cycler);
+  matrix = cdata->_matrix;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void UserVertexTransform::
+output(ostream &out) const {
+  out << get_name();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *UserVertexTransform::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               UserVertexTransform.
+////////////////////////////////////////////////////////////////////
+void UserVertexTransform::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void UserVertexTransform::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type UserVertexTransform is encountered
+//               in the Bam file.  It should create the UserVertexTransform
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *UserVertexTransform::
+make_from_bam(const FactoryParams &params) {
+  UserVertexTransform *object = new UserVertexTransform("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::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 UserVertexTransform.
+////////////////////////////////////////////////////////////////////
+void UserVertexTransform::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void UserVertexTransform::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserVertexTransform::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 UserVertexTransform.
+////////////////////////////////////////////////////////////////////
+void UserVertexTransform::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 96 - 0
panda/src/gobj/userVertexTransform.h

@@ -0,0 +1,96 @@
+// Filename: userVertexTransform.h
+// Created by:  drose (24Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 USERVERTEXTRANSFORM_H
+#define USERVERTEXTRANSFORM_H
+
+#include "pandabase.h"
+#include "vertexTransform.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : UserVertexTransform
+// Description : This is a specialization on VertexTransform that
+//               allows the user to specify any arbitrary transform
+//               matrix he likes.  This is rarely used except for
+//               testing.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA UserVertexTransform : public VertexTransform {
+PUBLISHED:
+  UserVertexTransform(const string &name);
+
+  INLINE const string &get_name() const;
+
+  INLINE void set_matrix(const LMatrix4f &matrix);
+  virtual void get_matrix(LMatrix4f &matrix) const;
+
+  virtual void output(ostream &out) const;
+
+private:
+  string _name;
+
+  // 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);
+
+    LMatrix4f _matrix;
+  };
+
+  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() {
+    VertexTransform::init_type();
+    register_type(_type_handle, "UserVertexTransform",
+                  VertexTransform::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 "userVertexTransform.I"
+
+#endif

+ 71 - 0
panda/src/gobj/vertexTransform.I

@@ -0,0 +1,71 @@
+// Filename: vertexTransform.I
+// Created by:  drose (23Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: VertexTransform::get_modified
+//       Access: Published
+//  Description: Returns a sequence number that's guaranteed to change
+//               at least every time the value reported by
+//               get_matrix() changes.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq VertexTransform::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::get_global_modified
+//       Access: Published, Static
+//  Description: Returns the currently highest
+//               VertexTransform::get_modified() value in the world.
+//               This can be used as a quick way to determine if any
+//               VertexTransforms have changed value recently.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq VertexTransform::
+get_global_modified() {
+  CDReader cdata(_global_cycler);
+  return cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE VertexTransform::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE VertexTransform::CData::
+CData(const VertexTransform::CData &copy) :
+  _modified(copy._modified)
+{
+}
+
+INLINE ostream &
+operator << (ostream &out, const VertexTransform &obj) {
+  obj.output(out);
+  return out;
+}

+ 217 - 0
panda/src/gobj/vertexTransform.cxx

@@ -0,0 +1,217 @@
+// Filename: vertexTransform.cxx
+// Created by:  drose (23Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "vertexTransform.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "indent.h"
+
+PipelineCycler<VertexTransform::CData> VertexTransform::_global_cycler;
+UpdateSeq VertexTransform::_next_modified;
+
+TypeHandle VertexTransform::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VertexTransform::
+VertexTransform() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VertexTransform::
+~VertexTransform() {
+  // We shouldn't destruct while any TransformPalettes are holding our
+  // pointer.
+  nassertv(_palettes.empty());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::accumulate_matrix
+//       Access: Published, Virtual
+//  Description: Adds the value of this transform's matrix, modified
+//               by the indicated weight, into the indicated
+//               accumulation matrix.  This is used to compute the
+//               result of several blended transforms.
+////////////////////////////////////////////////////////////////////
+void VertexTransform::
+accumulate_matrix(LMatrix4f &accum, float weight) const {
+  LMatrix4f me;
+  get_matrix(me);
+  
+  accum._m.m._00 += me._m.m._00 * weight;
+  accum._m.m._01 += me._m.m._01 * weight;
+  accum._m.m._02 += me._m.m._02 * weight;
+  accum._m.m._03 += me._m.m._03 * weight;
+  
+  accum._m.m._10 += me._m.m._10 * weight;
+  accum._m.m._11 += me._m.m._11 * weight;
+  accum._m.m._12 += me._m.m._12 * weight;
+  accum._m.m._13 += me._m.m._13 * weight;
+  
+  accum._m.m._20 += me._m.m._20 * weight;
+  accum._m.m._21 += me._m.m._21 * weight;
+  accum._m.m._22 += me._m.m._22 * weight;
+  accum._m.m._23 += me._m.m._23 * weight;
+  
+  accum._m.m._30 += me._m.m._30 * weight;
+  accum._m.m._31 += me._m.m._31 * weight;
+  accum._m.m._32 += me._m.m._32 * weight;
+  accum._m.m._33 += me._m.m._33 * weight;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexTransform::
+output(ostream &out) const {
+  out << get_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexTransform::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) 
+    << *this << ":\n";
+  LMatrix4f mat;
+  get_matrix(mat);
+  mat.write(out, indent_level + 2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::get_next_modified
+//       Access: Public, Static
+//  Description: Returns a monotonically increasing sequence.  Each
+//               time this is called, a new sequence number is
+//               returned, higher than the previous value.
+//
+//               This is used to ensure that all
+//               VertexTransform::get_modified() calls return an
+//               increasing number in the same space, so that
+//               TransformBlend::get_modified() is easy to determine.
+//               It is similar to Geom::get_modified(), but it is in a
+//               different space.
+////////////////////////////////////////////////////////////////////
+UpdateSeq VertexTransform::
+get_next_modified() {
+  ++_next_modified;
+
+  CDWriter cdatag(_global_cycler);
+  cdatag->_modified = _next_modified;
+
+  return _next_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::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_matrix() may not
+//               be propagated through the system.
+////////////////////////////////////////////////////////////////////
+void VertexTransform::
+mark_modified() {
+  CDWriter cdata(_cycler);
+  cdata->_modified = get_next_modified();
+  
+  Palettes::iterator pi;
+  for (pi = _palettes.begin(); pi != _palettes.end(); ++pi) {
+    (*pi)->update_modified(cdata->_modified);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void VertexTransform::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::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 VertexTransform.
+////////////////////////////////////////////////////////////////////
+void VertexTransform::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *VertexTransform::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void VertexTransform::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::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 VertexTransform::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexTransform::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 VertexTransform.
+////////////////////////////////////////////////////////////////////
+void VertexTransform::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 118 - 0
panda/src/gobj/vertexTransform.h

@@ -0,0 +1,118 @@
+// Filename: vertexTransform.h
+// Created by:  drose (23Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 VERTEXTRANSFORM_H
+#define VERTEXTRANSFORM_H
+
+#include "pandabase.h"
+#include "typedWritableReferenceCount.h"
+#include "updateSeq.h"
+#include "luse.h"
+#include "pset.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+
+class TransformPalette;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VertexTransform
+// Description : This is an abstract base class that holds a pointer
+//               to some transform, computed in some arbitrary way,
+//               that is to be applied to vertices during rendering.
+//               This is used to implement soft-skinned and animated
+//               vertices.  Derived classes will define how the
+//               transform is actually computed.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA VertexTransform : public TypedWritableReferenceCount {
+PUBLISHED:
+  VertexTransform();
+  virtual ~VertexTransform();
+
+  virtual void get_matrix(LMatrix4f &matrix) const=0;
+  virtual void accumulate_matrix(LMatrix4f &accum, float weight) const;
+
+  INLINE UpdateSeq get_modified() const;
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+  static UpdateSeq get_next_modified();
+  INLINE static UpdateSeq get_global_modified();
+
+protected:
+  void mark_modified();
+
+private:
+  typedef pset<TransformPalette *> Palettes;
+  Palettes _palettes;
+
+  // 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, "VertexTransform",
+                  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 TransformPalette;
+};
+
+INLINE ostream &operator << (ostream &out, const VertexTransform &obj);
+
+#include "vertexTransform.I"
+
+#endif

+ 1 - 0
panda/src/pgraph/cullableObject.cxx

@@ -38,6 +38,7 @@ munge_geom(const qpGeomMunger *munger) {
       _munger = munger;
       CPT(qpGeom) qpgeom = DCAST(qpGeom, _geom);
       qpgeom->munge_geom(munger, qpgeom, _munged_data);
+      _munged_data = _munged_data->compute_vertices();
       _geom = qpgeom;
     }
   }