瀏覽代碼

VertexRewriter; animation stats in cull

David Rose 21 年之前
父節點
當前提交
29d51a723b

+ 51 - 26
panda/src/char/character.cxx

@@ -33,7 +33,8 @@
 
 TypeHandle Character::_type_handle;
 
-PStatCollector Character::_anim_pcollector("App:Animation");
+PStatCollector Character::_app_animation_pcollector("App:Animation");
+PStatCollector Character::_cull_animation_pcollector("Cull:Animation");
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Character::Copy Constructor
@@ -46,7 +47,8 @@ Character(const Character &copy) :
   _cv(DynamicVertices::deep_copy(copy._cv)),
   _computed_vertices(copy._computed_vertices),
   _parts(copy._parts),
-  _char_pcollector(copy._char_pcollector)
+  _app_char_pcollector(copy._app_char_pcollector),
+  _cull_char_pcollector(copy._cull_char_pcollector)
 {
   // Now make a copy of the joint/slider hierarchy.  We could just use
   // the copy_subgraph feature of the PartBundleNode's copy
@@ -64,7 +66,8 @@ Character(const Character &copy) :
 Character::
 Character(const string &name) :
   PartBundleNode(name, new CharacterJointBundle(name)),
-  _char_pcollector(_anim_pcollector, name)
+  _app_char_pcollector(PStatCollector(_app_animation_pcollector, name), "Joints"),
+  _cull_char_pcollector(PStatCollector(_cull_animation_pcollector, name), "Joints")
 {
 }
 
@@ -158,7 +161,18 @@ cull_callback(CullTraverser *, CullTraverserData &) {
   // the view frustum.  We may need a better way to do this
   // optimization later, to handle characters that might animate
   // themselves in front of the view frustum.
-  update_to_now();
+
+  PStatTimer timer(_cull_char_pcollector);
+
+  double now = ClockObject::get_global_clock()->get_frame_time();
+  get_bundle()->advance_time(now);
+
+  if (char_cat.is_spam()) {
+    char_cat.spam() << "Animating " << *this << " at time " << now << "\n";
+  }
+
+  do_update();
+
   return true;
 }
 
@@ -193,24 +207,8 @@ update_to_now() {
 ////////////////////////////////////////////////////////////////////
 void Character::
 update() {
-  // Statistics
-  PStatTimer timer(_char_pcollector);
-
-  // First, update all the joints and sliders.
-  bool any_changed;
-  if (even_animation) {
-    any_changed = get_bundle()->force_update();
-  } else {
-    any_changed = get_bundle()->update();
-  }
-
-  // Now update the vertices, if we need to.  This is likely to be a
-  // slow operation.
-  if (any_changed || even_animation) {
-    if (_computed_vertices != (ComputedVertices *)NULL) {
-      _computed_vertices->update(this);
-    }
-  }
+  PStatTimer timer(_app_char_pcollector);
+  do_update();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -222,7 +220,7 @@ update() {
 void Character::
 force_update() {
   // Statistics
-  PStatTimer timer(_char_pcollector);
+  PStatTimer timer(_app_char_pcollector);
 
   // First, update all the joints and sliders.
   get_bundle()->force_update();
@@ -233,6 +231,31 @@ force_update() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Character::do_update
+//       Access: Private
+//  Description: The actual implementation of update().  Assumes the
+//               appropriate PStatCollector has already been started.
+////////////////////////////////////////////////////////////////////
+void Character::
+do_update() {
+  // First, update all the joints and sliders.
+  bool any_changed;
+  if (even_animation) {
+    any_changed = get_bundle()->force_update();
+  } else {
+    any_changed = get_bundle()->update();
+  }
+
+  // Now update the vertices, if we need to.  This is likely to be a
+  // slow operation.
+  if (any_changed || even_animation) {
+    if (_computed_vertices != (ComputedVertices *)NULL) {
+      _computed_vertices->update(this);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Character::copy_joints
 //       Access: Private
@@ -551,10 +574,12 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   }
 
 #ifdef DO_PSTATS
-  // Reinitialize our collector with our name, now that we know it.
+  // Reinitialize our collectors with our name, now that we know it.
   if (has_name()) {
-    _char_pcollector =
-      PStatCollector(_anim_pcollector, get_name());
+    _app_char_pcollector = 
+      PStatCollector(PStatCollector(_app_animation_pcollector, get_name()), "Joints");
+    _cull_char_pcollector = 
+      PStatCollector(PStatCollector(_cull_animation_pcollector, get_name()), "Joints");
   }
 #endif
 }

+ 5 - 2
panda/src/char/character.h

@@ -67,6 +67,7 @@ PUBLISHED:
   void force_update();
 
 private:
+  void do_update();
   void copy_joints(PartGroup *copy, PartGroup *orig);
 
   typedef pmap<const PandaNode *, PandaNode *> NodeMap;
@@ -94,8 +95,10 @@ private:
   Parts _parts;
 
   // Statistics
-  PStatCollector _char_pcollector;
-  static PStatCollector _anim_pcollector;
+  PStatCollector _app_char_pcollector;
+  PStatCollector _cull_char_pcollector;
+  static PStatCollector _app_animation_pcollector;
+  static PStatCollector _cull_animation_pcollector;
 
 public:
   static void register_with_read_factory();

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

@@ -36,6 +36,7 @@
     qpgeomVertexDataType.h qpgeomVertexDataType.I \
     qpgeomVertexFormat.h qpgeomVertexFormat.I \
     qpgeomVertexReader.h qpgeomVertexReader.I \
+    qpgeomVertexRewriter.h qpgeomVertexRewriter.I \
     qpgeomVertexWriter.h qpgeomVertexWriter.I \
     indexBufferContext.I indexBufferContext.h \
     internalName.I internalName.h \
@@ -82,6 +83,7 @@
     qpgeomVertexDataType.cxx \
     qpgeomVertexFormat.cxx \
     qpgeomVertexReader.cxx \
+    qpgeomVertexRewriter.cxx \
     qpgeomVertexWriter.cxx \
     indexBufferContext.cxx \
     material.cxx  \
@@ -126,6 +128,7 @@
     qpgeomVertexDataType.h qpgeomVertexDataType.I \
     qpgeomVertexFormat.h qpgeomVertexFormat.I \
     qpgeomVertexReader.h qpgeomVertexReader.I \
+    qpgeomVertexRewriter.h qpgeomVertexRewriter.I \
     qpgeomVertexWriter.h qpgeomVertexWriter.I \
     indexBufferContext.I indexBufferContext.h \
     internalName.I internalName.h \

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

@@ -28,4 +28,5 @@
 #include "qpgeomVertexDataType.cxx"
 #include "qpgeomVertexFormat.cxx"
 #include "qpgeomVertexReader.cxx"
+#include "qpgeomVertexRewriter.cxx"
 #include "qpgeomVertexWriter.cxx"

+ 41 - 1
panda/src/gobj/qpgeomVertexData.I

@@ -38,7 +38,10 @@ get_name() const {
 INLINE void qpGeomVertexData::
 set_name(const string &name) {
   _name = name;
-  _this_animate_vertices_pcollector = PStatCollector(_animate_vertices_pcollector, name);
+  _app_char_pcollector = 
+    PStatCollector(PStatCollector(_app_animation_pcollector, name), "Vertices");
+  _cull_char_pcollector =
+    PStatCollector(PStatCollector(_cull_animation_pcollector, name), "Vertices");
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -160,6 +163,43 @@ get_modified() const {
   return cdata->_modified;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::animate_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).
+////////////////////////////////////////////////////////////////////
+INLINE CPT(qpGeomVertexData) qpGeomVertexData::
+animate_vertices() const {
+  return do_animate_vertices(true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::animate_vertices_cull
+//       Access: Public
+//  Description: Does exactly the same thing as animate_vertices(),
+//               but when PStats is enabled, it records the time spent
+//               as during the cull step instead of the app step.
+//               This is intended to be called from the cull callback,
+//               rather the called directly by the user.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(qpGeomVertexData) qpGeomVertexData::
+animate_vertices_cull() const {
+  return do_animate_vertices(false);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::pack_8888
 //       Access: Public, Static

+ 54 - 59
panda/src/gobj/qpgeomVertexData.cxx

@@ -30,7 +30,8 @@ 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::_animate_vertices_pcollector("Cull:Animate vertices");
+PStatCollector qpGeomVertexData::_app_animation_pcollector("App:Animation");
+PStatCollector qpGeomVertexData::_cull_animation_pcollector("Cull:Animation");
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::Default Constructor
@@ -40,7 +41,8 @@ PStatCollector qpGeomVertexData::_animate_vertices_pcollector("Cull:Animate vert
 ////////////////////////////////////////////////////////////////////
 qpGeomVertexData::
 qpGeomVertexData() :
-  _this_animate_vertices_pcollector(_animate_vertices_pcollector)
+  _app_char_pcollector(_app_animation_pcollector),
+  _cull_char_pcollector(_cull_animation_pcollector)
 {
 }
 
@@ -56,7 +58,8 @@ qpGeomVertexData(const string &name,
   _name(name),
   _format(format),
   _usage_hint(usage_hint),
-  _this_animate_vertices_pcollector(_animate_vertices_pcollector, name)
+  _app_char_pcollector(PStatCollector(_app_animation_pcollector, name), "Vertices"),
+  _cull_char_pcollector(PStatCollector(_cull_animation_pcollector, name), "Vertices")
 {
   nassertv(_format->is_registered());
 
@@ -82,7 +85,8 @@ qpGeomVertexData(const qpGeomVertexData &copy) :
   _name(copy._name),
   _format(copy._format),
   _cycler(copy._cycler),
-  _this_animate_vertices_pcollector(copy._this_animate_vertices_pcollector)
+  _app_char_pcollector(copy._app_char_pcollector),
+  _cull_char_pcollector(copy._cull_char_pcollector)
 {
 }
   
@@ -97,7 +101,8 @@ operator = (const qpGeomVertexData &copy) {
   _name = copy._name;
   _format = copy._format;
   _cycler = copy._cycler;
-  _this_animate_vertices_pcollector = copy._this_animate_vertices_pcollector;
+  _app_char_pcollector = copy._app_char_pcollector;
+  _cull_char_pcollector = copy._cull_char_pcollector;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -374,10 +379,10 @@ convert_to(const qpGeomVertexFormat *new_format) const {
               << "generic copy " << *new_data_type << " from " 
               << *data_type << "\n";
           }
-          qpGeomVertexReader from(this);
-          from.set_data_type(i, data_type);
           qpGeomVertexWriter to(new_data);
           to.set_data_type(new_i, new_data_type);
+          qpGeomVertexReader from(this);
+          from.set_data_type(i, data_type);
 
           while (!from.is_at_end()) {
             to.set_data4f(from.get_data4f());
@@ -424,8 +429,8 @@ scale_color(const LVecBase4f &color_scale, int num_components,
      contents, get_usage_hint(), true);
 
   // Now go through and apply the scale, copying it to the new data.
-  qpGeomVertexReader from(this, InternalName::get_color());
   qpGeomVertexWriter to(new_data, InternalName::get_color());
+  qpGeomVertexReader from(this, InternalName::get_color());
 
   for (int i = 0; i < num_vertices; i++) {
     Colorf color = from.get_data4f();
@@ -475,49 +480,6 @@ set_color(const Colorf &color, int num_components,
   return new_data;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::animate_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::
-animate_vertices() const {
-  CDReader cdata(_cycler);
-  if (cdata->_transform_blend_palette == (TransformBlendPalette *)NULL) {
-    // No vertex animation.
-    return this;
-  }
-
-  if (cdata->_animated_vertices == (qpGeomVertexData *)NULL) {
-    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
-    ((qpGeomVertexData *)this)->make_animated_vertices(cdataw);
-    return cdataw->_animated_vertices;
-  } else {
-    UpdateSeq blend_modified = cdata->_transform_blend_palette->get_modified();
-    if (cdata->_animated_vertices_modified == blend_modified) {
-      // No changes.
-      return cdata->_animated_vertices;
-    }
-    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
-    cdataw->_animated_vertices_modified = blend_modified;
-    ((qpGeomVertexData *)this)->update_animated_vertices(cdataw);
-    return cdataw->_animated_vertices;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::replace_data_type
 //       Access: Published
@@ -673,6 +635,38 @@ get_array_info(const InternalName *name,
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::do_animate_vertices
+//       Access: Private
+//  Description: This is the private implementation of
+//               animate_vertices() and animate_vertices_cull().
+////////////////////////////////////////////////////////////////////
+CPT(qpGeomVertexData) qpGeomVertexData::
+do_animate_vertices(bool from_app) const {
+  CDReader cdata(_cycler);
+  if (cdata->_transform_blend_palette == (TransformBlendPalette *)NULL) {
+    // No vertex animation.
+    return this;
+  }
+
+  if (cdata->_animated_vertices == (qpGeomVertexData *)NULL) {
+    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
+    ((qpGeomVertexData *)this)->make_animated_vertices(cdataw);
+    ((qpGeomVertexData *)this)->update_animated_vertices(cdataw, from_app);
+    return cdataw->_animated_vertices;
+  } else {
+    UpdateSeq blend_modified = cdata->_transform_blend_palette->get_modified();
+    if (cdata->_animated_vertices_modified == blend_modified) {
+      // No changes.
+      return cdata->_animated_vertices;
+    }
+    CDWriter cdataw(((qpGeomVertexData *)this)->_cycler, cdata);
+    cdataw->_animated_vertices_modified = blend_modified;
+    ((qpGeomVertexData *)this)->update_animated_vertices(cdataw, from_app);
+    return cdataw->_animated_vertices;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::bytewise_copy
 //       Access: Private, Static
@@ -807,9 +801,6 @@ make_animated_vertices(qpGeomVertexData::CDWriter &cdata) {
     (InternalName::get_transform_blend(), 0, qpGeomVertexDataType::NT_uint16,
      qpGeomVertexDataType::C_index,
      min(get_usage_hint(), qpGeomUsageHint::UH_dynamic), false);
-
-  // Now fill it up with the appropriate data.
-  update_animated_vertices(cdata);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -820,7 +811,7 @@ make_animated_vertices(qpGeomVertexData::CDWriter &cdata) {
 //               existing animated_vertices object.
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
-update_animated_vertices(qpGeomVertexData::CDWriter &cdata) {
+update_animated_vertices(qpGeomVertexData::CDWriter &cdata, bool from_app) {
   int num_vertices = get_num_vertices();
 
   if (gobj_cat.is_debug()) {
@@ -829,12 +820,16 @@ update_animated_vertices(qpGeomVertexData::CDWriter &cdata) {
       << "\n";
   }
 
-  PStatTimer timer(_this_animate_vertices_pcollector);
+#ifdef DO_PSTATS
+  PStatCollector &collector = from_app ? _app_char_pcollector : _cull_char_pcollector;
+  PStatTimer timer(collector);
+#endif
 
   CPT(TransformBlendPalette) palette = cdata->_transform_blend_palette;
   nassertv(palette != (TransformBlendPalette *)NULL);
 
-  // Recompute all the blends up front.
+  // Recompute all the blends up front, so we don't have to test each
+  // one for staleness at each vertex.
   int num_blends = palette->get_num_blends();
   int bi;
   for (bi = 0; bi < num_blends; bi++) {
@@ -843,10 +838,10 @@ update_animated_vertices(qpGeomVertexData::CDWriter &cdata) {
 
   PT(qpGeomVertexData) new_data = cdata->_animated_vertices;
 
-  // Now go through and apply the scale, copying it to the new data.
+  // Now go through and compute the animation.
+  qpGeomVertexWriter to(new_data, InternalName::get_vertex());
   qpGeomVertexReader from(this, InternalName::get_vertex());
   qpGeomVertexReader blendi(this, InternalName::get_transform_blend());
-  qpGeomVertexWriter to(new_data, InternalName::get_vertex());
 
   if (from.get_data_type()->get_num_values() == 4) {
     for (int i = 0; i < num_vertices; i++) {

+ 10 - 4
panda/src/gobj/qpgeomVertexData.h

@@ -104,7 +104,7 @@ PUBLISHED:
               qpGeomVertexDataType::NumericType numeric_type,
               qpGeomVertexDataType::Contents contents) const;
 
-  CPT(qpGeomVertexData) animate_vertices() const;
+  INLINE CPT(qpGeomVertexData) animate_vertices() const;
 
   PT(qpGeomVertexData) 
     replace_data_type(const InternalName *name, int num_components,
@@ -117,6 +117,8 @@ PUBLISHED:
   void write(ostream &out, int indent_level = 0) const;
 
 public:
+  INLINE CPT(qpGeomVertexData) animate_vertices_cull() const;
+
   bool get_array_info(const InternalName *name, 
                       const qpGeomVertexArrayData *&array_data,
                       int &num_components,
@@ -131,6 +133,8 @@ public:
   static INLINE unsigned int unpack_8888_d(PN_uint32 data);
 
 private:
+  CPT(qpGeomVertexData) do_animate_vertices(bool from_app) const;
+
   static void bytewise_copy(unsigned char *to, int to_stride,
                             const unsigned char *from, int from_stride,
                             const qpGeomVertexDataType *from_type,
@@ -175,14 +179,16 @@ private:
 private:
   bool do_set_num_vertices(int n, CDWriter &cdata);
   void make_animated_vertices(CDWriter &cdata);
-  void update_animated_vertices(CDWriter &cdata);
+  void update_animated_vertices(CDWriter &cdata, bool from_app);
 
   static PStatCollector _convert_pcollector;
   static PStatCollector _scale_color_pcollector;
   static PStatCollector _set_color_pcollector;
-  static PStatCollector _animate_vertices_pcollector;
+  static PStatCollector _app_animation_pcollector;
+  static PStatCollector _cull_animation_pcollector;
 
-  PStatCollector _this_animate_vertices_pcollector;
+  PStatCollector _app_char_pcollector;
+  PStatCollector _cull_char_pcollector;
 
 public:
   static void register_with_read_factory();

+ 2 - 3
panda/src/gobj/qpgeomVertexReader.I

@@ -331,8 +331,7 @@ set_pointer(int vertex) {
   nassertv(_data_type != (qpGeomVertexDataType *)NULL);
   _read_vertex = vertex;
   CPT(qpGeomVertexArrayData) array_data = _vertex_data->get_array(_array);
-  _data = array_data->get_data();
-  _pointer = _data.p() + _data_type->get_start() + _stride * _read_vertex;
+  _pointer = array_data->get_data() + _data_type->get_start() + _stride * _read_vertex;
   _num_vertices = array_data->get_num_vertices();
 }
 
@@ -345,7 +344,7 @@ set_pointer(int vertex) {
 INLINE const unsigned char *qpGeomVertexReader::
 inc_pointer() {
   nassertr(_read_vertex < _num_vertices, NULL);
-  nassertr(_pointer == _vertex_data->get_array(_array)->get_data().p() + _data_type->get_start() + _stride * _read_vertex, NULL);
+  nassertr(_pointer == _vertex_data->get_array(_array)->get_data() + _data_type->get_start() + _stride * _read_vertex, NULL);
 
   const unsigned char *orig_pointer = _pointer;
   _pointer += _stride;

+ 16 - 1
panda/src/gobj/qpgeomVertexReader.h

@@ -40,6 +40,22 @@
 //               for each vertex, it is faster to use a different
 //               GeomVertexReader for each data type.
 //
+//               Note that a GeomVertexReader does not keep a
+//               reference count to the actual vertex data buffer (it
+//               grabs the current data buffer from the GeomVertexData
+//               whenever set_data_type() is called).  This means that
+//               it is important not to keep a GeomVertexReader object
+//               around over a long period of time in which the data
+//               buffer is likely to be deallocated; it is intended
+//               for making a quick pass over the data in one session.
+//
+//               It also means that you should create any
+//               GeomVertexWriters *before* creating GeomVertexReaders
+//               on the same data, since the writer itself might cause
+//               the vertex buffer to be deallocated.  Better yet, use
+//               a GeomVertexRewriter if you are going to create both
+//               of them anyway.
+//
 //               This is part of the experimental Geom rewrite.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA qpGeomVertexReader {
@@ -88,7 +104,6 @@ private:
   const qpGeomVertexDataType *_data_type;
   int _stride;
 
-  CPTA_uchar _data;
   const unsigned char *_pointer;
 
   int _start_vertex;

+ 261 - 0
panda/src/gobj/qpgeomVertexRewriter.I

@@ -0,0 +1,261 @@
+// Filename: qpgeomVertexRewriter.I
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::Constructor
+//       Access: Published
+//  Description: Constructs a new rewriter to process the vertices of
+//               the indicated data object.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexRewriter::
+qpGeomVertexRewriter(qpGeomVertexData *vertex_data) :
+  qpGeomVertexWriter(vertex_data),
+  qpGeomVertexReader(vertex_data)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::Constructor
+//       Access: Published
+//  Description: Constructs a new rewriter to process the vertices of
+//               the indicated data object.  This flavor creates the
+//               rewriter specifically to process the named data type.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexRewriter::
+qpGeomVertexRewriter(qpGeomVertexData *vertex_data, const string &name) :
+  qpGeomVertexWriter(vertex_data),
+  qpGeomVertexReader(vertex_data)
+{
+  set_data_type(name);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::Constructor
+//       Access: Published
+//  Description: Constructs a new rewriter to process the vertices of
+//               the indicated data object.  This flavor creates the
+//               rewriter specifically to process the named data type.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexRewriter::
+qpGeomVertexRewriter(qpGeomVertexData *vertex_data, const InternalName *name) :
+  qpGeomVertexWriter(vertex_data),
+  qpGeomVertexReader(vertex_data)
+{
+  set_data_type(name);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexRewriter::
+~qpGeomVertexRewriter() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::get_vertex_data
+//       Access: Published
+//  Description: Returns the vertex data object that the
+//               rewriter is processing.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexData *qpGeomVertexRewriter::
+get_vertex_data() const {
+  nassertr(qpGeomVertexWriter::get_vertex_data() == 
+           qpGeomVertexReader::get_vertex_data(), NULL);
+  return qpGeomVertexWriter::get_vertex_data();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::set_data_type
+//       Access: Published
+//  Description: Sets up the rewriter to use the nth data type of the
+//               GeomVertexFormat, numbering from 0.
+//
+//               This also resets both the read and write vertex
+//               numbers to the start vertex (the same value passed to
+//               a previous call to set_vertex(), or 0 if set_vertex()
+//               was never called.)
+//
+//               The return value is true if the data type is valid,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexRewriter::
+set_data_type(int data_type) {
+  // It's important to invoke the writer first, then the reader.  See
+  // set_vertex().
+  qpGeomVertexWriter::set_data_type(data_type);
+  return qpGeomVertexReader::set_data_type(data_type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::set_data_type
+//       Access: Published
+//  Description: Sets up the rewriter to use the data type with the
+//               indicated name.
+//
+//               This also resets both the read and write vertex
+//               numbers to the start vertex (the same value passed to
+//               a previous call to set_vertex(), or 0 if set_vertex()
+//               was never called.)
+//
+//               The return value is true if the data type is valid,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexRewriter::
+set_data_type(const string &name) {
+  return set_data_type(InternalName::make(name));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::set_data_type
+//       Access: Published
+//  Description: Sets up the rewriter to use the data type with the
+//               indicated name.
+//
+//               This also resets both the read and write vertex
+//               numbers to the start vertex (the same value passed to
+//               a previous call to set_vertex(), or 0 if set_vertex()
+//               was never called.)
+//
+//               The return value is true if the data type is valid,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexRewriter::
+set_data_type(const InternalName *name) {
+  // It's important to invoke the writer first, then the reader.  See
+  // set_vertex().
+  qpGeomVertexWriter::set_data_type(name);
+  return qpGeomVertexReader::set_data_type(name);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::set_data_type
+//       Access: Published
+//  Description: Sets up the rewriter to use the indicated data_type
+//               description on the given array.
+//
+//               This also resets both the read and write vertex
+//               numbers to the start vertex (the same value passed to
+//               a previous call to set_vertex(), or 0 if set_vertex()
+//               was never called.)
+//
+//               The return value is true if the data type is valid,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool qpGeomVertexRewriter::
+set_data_type(int array, const qpGeomVertexDataType *data_type) {
+  // It's important to invoke the writer first, then the reader.  See
+  // set_vertex().
+  qpGeomVertexWriter::set_data_type(array, data_type);
+  return qpGeomVertexReader::set_data_type(array, data_type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::has_data_type
+//       Access: Published
+//  Description: Returns true if a valid data type has been
+//               successfully set, or false if the data type does not
+//               exist.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexRewriter::
+has_data_type() const {
+  nassertr(qpGeomVertexWriter::get_data_type() == 
+           qpGeomVertexReader::get_data_type(), false);
+  return qpGeomVertexWriter::has_data_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::get_array
+//       Access: Published
+//  Description: Returns the array index containing the data type that
+//               the rewriter is working on.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexRewriter::
+get_array() const {
+  nassertr(qpGeomVertexWriter::get_array() == 
+           qpGeomVertexReader::get_array(), -1);
+  return qpGeomVertexWriter::get_array();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::get_data_type
+//       Access: Published
+//  Description: Returns the description of the data type that the
+//               rewriter is working on.
+////////////////////////////////////////////////////////////////////
+INLINE const qpGeomVertexDataType *qpGeomVertexRewriter::
+get_data_type() const {
+  nassertr(qpGeomVertexWriter::get_data_type() == 
+           qpGeomVertexReader::get_data_type(), NULL);
+  return qpGeomVertexWriter::get_data_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::set_vertex
+//       Access: Published
+//  Description: Sets the start, write, and write index to the
+//               indicated value.  The rewriter will begin traversing
+//               from the given vertex.
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomVertexRewriter::
+set_vertex(int vertex) {
+  // It's important to invoke the Writer first, since that might force
+  // a recopy of the array, which might invalidate the pointer already
+  // stored by the Reader if we invoked the Reader first.
+  qpGeomVertexWriter::set_vertex(vertex);
+  qpGeomVertexReader::set_vertex(vertex);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::get_start_vertex
+//       Access: Published
+//  Description: Returns the vertex index at which the rewriter
+//               started.  It will return to this vertex if you reset
+//               the current data_type.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexRewriter::
+get_start_vertex() const {
+  nassertr(qpGeomVertexWriter::get_start_vertex() == 
+           qpGeomVertexReader::get_start_vertex(), 0);
+  return qpGeomVertexWriter::get_start_vertex();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::get_num_vertices
+//       Access: Published
+//  Description: Returns the number of vertices in the vertex data.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexRewriter::
+get_num_vertices() const {
+  nassertr(qpGeomVertexWriter::get_num_vertices() == 
+           qpGeomVertexReader::get_num_vertices(), 0);
+  return qpGeomVertexWriter::get_num_vertices();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexRewriter::is_at_end
+//       Access: Published
+//  Description: Returns true if the reader or writer is currently at
+//               the end of the list of vertices, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexRewriter::
+is_at_end() const {
+  return qpGeomVertexWriter::is_at_end() || qpGeomVertexReader::is_at_end();
+}

+ 19 - 0
panda/src/gobj/qpgeomVertexRewriter.cxx

@@ -0,0 +1,19 @@
+// Filename: qpgeomVertexRewriter.cxx
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpgeomVertexRewriter.h"

+ 73 - 0
panda/src/gobj/qpgeomVertexRewriter.h

@@ -0,0 +1,73 @@
+// Filename: qpgeomVertexRewriter.h
+// Created by:  drose (28Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpGEOMVERTEXREWRITER_H
+#define qpGEOMVERTEXREWRITER_H
+
+#include "pandabase.h"
+#include "qpgeomVertexReader.h"
+#include "qpgeomVertexWriter.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpGeomVertexRewriter
+// Description : This object provides the functionality of both a
+//               GeomVertexReader and a GeomVertexWriter, combined
+//               together into one convenient package.  It is designed
+//               for making a single pass over a GeomVertexData
+//               object, modifying vertices as it goes.
+//
+//               Although it doesn't provide any real performance
+//               benefit over using a separate reader and writer
+//               object, it should probably be used in preference to
+//               separate objects, because it makes an effort to
+//               manage the reference counts properly between the
+//               reader and the writer to avoid accidentally
+//               dereferencing either array while recopying.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpGeomVertexRewriter : public qpGeomVertexWriter, public qpGeomVertexReader {
+PUBLISHED:
+  INLINE qpGeomVertexRewriter(qpGeomVertexData *vertex_data);
+  INLINE qpGeomVertexRewriter(qpGeomVertexData *vertex_data,
+                              const string &name);
+  INLINE qpGeomVertexRewriter(qpGeomVertexData *vertex_data,
+                              const InternalName *name);
+  INLINE ~qpGeomVertexRewriter();
+
+  INLINE qpGeomVertexData *get_vertex_data() const;
+
+  INLINE bool set_data_type(int data_type);
+  INLINE bool set_data_type(const string &name);
+  INLINE bool set_data_type(const InternalName *name);
+  INLINE bool set_data_type(int array, const qpGeomVertexDataType *data_type);
+
+  INLINE bool has_data_type() const;
+  INLINE int get_array() const;
+  INLINE const qpGeomVertexDataType *get_data_type() const;
+
+  INLINE void set_vertex(int vertex);
+
+  INLINE int get_start_vertex() const;
+  INLINE int get_num_vertices() const;
+  INLINE bool is_at_end() const;
+};
+
+#include "qpgeomVertexRewriter.I"
+
+#endif

+ 2 - 3
panda/src/gobj/qpgeomVertexWriter.I

@@ -500,8 +500,7 @@ set_pointer(int vertex) {
   nassertv(_data_type != (qpGeomVertexDataType *)NULL);
   _write_vertex = vertex;
   PT(qpGeomVertexArrayData) array_data = _vertex_data->modify_array(_array);
-  _data = array_data->modify_data();
-  _pointer = _data.p() + _data_type->get_start() + _stride * _write_vertex;
+  _pointer = array_data->modify_data() + _data_type->get_start() + _stride * _write_vertex;
   _num_vertices = array_data->get_num_vertices();
 }
 
@@ -514,7 +513,7 @@ set_pointer(int vertex) {
 INLINE unsigned char *qpGeomVertexWriter::
 inc_pointer() {
   nassertr(_write_vertex < _num_vertices, NULL);
-  nassertr(_pointer == _vertex_data->get_array(_array)->get_data().p() + _data_type->get_start() + _stride * _write_vertex, NULL);
+  nassertr(_pointer == _vertex_data->get_array(_array)->get_data() + _data_type->get_start() + _stride * _write_vertex, NULL);
 
   unsigned char *orig_pointer = _pointer;
   _pointer += _stride;

+ 15 - 1
panda/src/gobj/qpgeomVertexWriter.h

@@ -53,6 +53,21 @@
 //               vertex, it is faster to use a different
 //               GeomVertexWriter for each data type.
 //
+//               Note that, like a GeomVertexReader, a
+//               GeomVertexWriter does not keep a reference count to
+//               the actual vertex data buffer.  This means that it is
+//               important not to keep a GeomVertexWriter object
+//               around over a long period of time in which the data
+//               buffer is likely to be deallocated; it is intended
+//               for making a quick pass over the data in one session.
+//
+//               It also means that you should create any
+//               GeomVertexWriters *before* creating GeomVertexReaders
+//               on the same data, since the writer itself might cause
+//               the vertex buffer to be deallocated.  Better yet, use
+//               a GeomVertexRewriter if you are going to create both
+//               of them anyway.
+//
 //               This is part of the experimental Geom rewrite.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA qpGeomVertexWriter {
@@ -115,7 +130,6 @@ private:
   const qpGeomVertexDataType *_data_type;
   int _stride;
 
-  PTA_uchar _data;
   unsigned char *_pointer;
 
   int _start_vertex;

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

@@ -38,7 +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->animate_vertices();
+      _munged_data = _munged_data->animate_vertices_cull();
       _geom = qpgeom;
     }
   }