Browse Source

flatten optimization; also SceneGraphReducer::make_compatible_format()

David Rose 18 years ago
parent
commit
b9b1e78399

+ 2 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -49,6 +49,7 @@
 #include "fog.h"
 #include "fogAttrib.h"
 #include "geomNode.h"
+#include "geomTransformer.h"
 #include "lensNode.h"
 #include "light.h"
 #include "lightAttrib.h"
@@ -397,6 +398,7 @@ init_libpgraph() {
   Fog::init_type();
   FogAttrib::init_type();
   GeomNode::init_type();
+  GeomTransformer::init_type();
   LensNode::init_type();
   Light::init_type();
   LightAttrib::init_type();

+ 347 - 166
panda/src/pgraph/geomTransformer.cxx

@@ -38,6 +38,8 @@ PStatCollector GeomTransformer::_apply_set_color_collector("*:Flatten:apply:set
 PStatCollector GeomTransformer::_apply_scale_color_collector("*:Flatten:apply:scale color");
 PStatCollector GeomTransformer::_apply_set_format_collector("*:Flatten:apply:set format");
 
+TypeHandle GeomTransformer::NewCollectedData::_type_handle;
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomTransformer::Constructor
 //       Access: Public
@@ -68,6 +70,7 @@ GeomTransformer(const GeomTransformer &copy) :
 ////////////////////////////////////////////////////////////////////
 GeomTransformer::
 ~GeomTransformer() {
+  finish_collect(false);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -580,9 +583,18 @@ reverse(GeomNode *node) {
 //  Description: Collects together GeomVertexDatas from different
 //               geoms into one big (or several big) GeomVertexDatas.
 //               Returns the number of unique GeomVertexDatas created.
+//
+//               If format_only is true, this only makes
+//               GeomVertexFormats compatible; it does not otherwise
+//               combine vertices.
+//
+//               You should follow this up with a call to
+//               finish_collect(), but you probably don't want to call
+//               this method directly anyway.  Call
+//               SceneGraphReducer::collect_vertex_data() instead.
 ////////////////////////////////////////////////////////////////////
 int GeomTransformer::
-collect_vertex_data(Geom *geom, int collect_bits) {
+collect_vertex_data(Geom *geom, int collect_bits, bool format_only) {
   CPT(GeomVertexData) vdata = geom->get_vertex_data();
   if (vdata->get_num_rows() > _max_collect_vertices) {
     // Don't even bother.
@@ -609,71 +621,311 @@ collect_vertex_data(Geom *geom, int collect_bits) {
     key._animation_type = Geom::AT_none;
   }
 
-  AlreadyCollected::const_iterator ai;
-  ai = _already_collected.find(vdata);
-  if (ai != _already_collected.end()) {
+  AlreadyCollectedMap::const_iterator ai;
+  ai = _already_collected_map.find(vdata);
+  if (ai != _already_collected_map.end()) {
     // We've previously collected this vertex data; reuse it.
     const AlreadyCollectedData &acd = (*ai).second;
-    geom->offset_vertices(acd._data, acd._offset);
+    SourceGeom source_geom;
+    source_geom._geom = geom;
+    source_geom._vertex_offset = acd._vertex_offset;
+    acd._ncd->_source_geoms.push_back(source_geom);
     return 0;
   }
 
-  // We haven't collected this vertex data yet; append the vertices
-  // onto the new data.
-  int num_created = 0;
-
-  NewCollectedData::iterator ni = _new_collected_data.find(key);
-  PT(GeomVertexData) new_data;
-  if (ni != _new_collected_data.end()) {
-    new_data = (*ni).second;
+  // We haven't collected this vertex data yet; associate it with a
+  // new data.
+  NewCollectedMap::iterator ni = _new_collected_map.find(key);
+  NewCollectedData *ncd;
+  if (ni != _new_collected_map.end()) {
+    ncd = (*ni).second;
 
   } else {
     // We haven't encountered a compatible GeomVertexData before.
-    // Create a new one.
-    new_data = new GeomVertexData(vdata->get_name(), format, 
-				  vdata->get_usage_hint());
-    _new_collected_data[key] = new_data;
-    ++num_created;
+    // Create a new entry.
+    ncd = new NewCollectedData(vdata);
+    _new_collected_list.push_back(ncd);
+    _new_collected_map[key] = ncd;
   }
 
-  int offset = new_data->get_num_rows();
-  int new_num_vertices = offset + vdata->get_num_rows();
-  if (new_num_vertices > _max_collect_vertices) {
+  if (ncd->_new_format != format) {
+    ncd->_new_format = format->get_union_format(ncd->_new_format);
+  }
+
+  int this_num_vertices = vdata->get_num_rows();
+
+  if (!format_only &&
+      ncd->_num_vertices + this_num_vertices > _max_collect_vertices) {
     // Whoa, hold the phone!  Too many vertices going into this one
     // GeomVertexData object; we'd better start over.
-    new_data = new GeomVertexData(vdata->get_name(), format, 
-				  vdata->get_usage_hint());
-    _new_collected_data[key] = new_data;
-    offset = 0;
-    new_num_vertices = vdata->get_num_rows();
-    ++num_created;
-  }
-
-  if (new_data->get_format() != format) {
-    // We're combining two GeomVertexDatas of different formats.
-    // Therefore, we need to expand the format to include the union of
-    // both sets of columns.
-    CPT(GeomVertexFormat) new_format = format->get_union_format(new_data->get_format());
-    new_data->set_format(new_format);
-    nassertr(offset == new_data->get_num_rows(), 0);
+    ncd = new NewCollectedData(vdata);
+    _new_collected_list.push_back(ncd);
+    _new_collected_map[key] = ncd;
+  }
+
+  int vertex_offset = ncd->_num_vertices;
+
+  AlreadyCollectedData &acd = _already_collected_map[vdata];
+  acd._ncd = ncd;
+  acd._vertex_offset = vertex_offset;
+
+  SourceGeom source_geom;
+  source_geom._geom = geom;
+  source_geom._vertex_offset = vertex_offset;
+  ncd->_source_geoms.push_back(source_geom);
+  
+  SourceData source_data;
+  source_data._vdata = vdata;
+  source_data._num_vertices = this_num_vertices;
+
+  ncd->_source_datas.push_back(source_data);
+  ncd->_num_vertices += this_num_vertices;
 
-    // Also, convert (non-destructively) the current Geom's vertex
-    // data to the new format, so we can just blindly append the
-    // vertices to new_data, in the lines below.
-    vdata = vdata->convert_to(new_format);
-    format = new_format;
+  return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::collect_vertex_data
+//       Access: Public
+//  Description: Collects together individual GeomVertexData
+//               structures that share the same format into one big
+//               GeomVertexData structure.  This is intended to
+//               minimize context switches on the graphics card.
+//
+//               If format_only is true, this only makes
+//               GeomVertexFormats compatible; it does not otherwise
+//               combine vertices.
+//
+//               You should follow this up with a call to
+//               finish_collect(), but you probably don't want to call
+//               this method directly anyway.  Call
+//               SceneGraphReducer::collect_vertex_data() instead.
+////////////////////////////////////////////////////////////////////
+int GeomTransformer::
+collect_vertex_data(GeomNode *node, int collect_bits, bool format_only) {
+  int num_adjusted = 0;
+  GeomTransformer *dynamic = NULL;
+
+  GeomNode::CDWriter cdata(node->_cycler);
+  GeomNode::GeomList::iterator gi;
+  GeomNode::GeomList &geoms = *(cdata->modify_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
+    GeomNode::GeomEntry &entry = (*gi);
+    PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
+    entry._geom = new_geom;
+
+    if ((collect_bits & SceneGraphReducer::CVD_avoid_dynamic) != 0 &&
+        new_geom->get_vertex_data()->get_usage_hint() < Geom::UH_static) {
+      // This one has some dynamic properties.  Collect it
+      // independently of the outside world.
+      if (dynamic == (GeomTransformer *)NULL) {
+        dynamic = new GeomTransformer(*this);
+      }
+      num_adjusted += dynamic->collect_vertex_data(new_geom, collect_bits, format_only);
+      
+    } else {
+      num_adjusted += collect_vertex_data(new_geom, collect_bits, format_only);
+    }
   }
 
-  new_data->set_num_rows(new_num_vertices);
+  if (dynamic != (GeomTransformer *)NULL) {
+    num_adjusted += dynamic->finish_collect(format_only);
+    delete dynamic;
+  }
+
+  return num_adjusted;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::finish_collect
+//       Access: Public
+//  Description: This should be called after a call to
+//               collect_vertex_data() to finalize the changes and
+//               apply them to the vertices in the graph.  If this is
+//               not called, it will be called automatically by the
+//               GeomTransformer destructor.
+//
+//               If format_only is true, this returns the number of
+//               GeomVertexDatas modified to use a new format.  If
+//               false, it returns the number of GeomVertexDatas
+//               created.
+////////////////////////////////////////////////////////////////////
+int GeomTransformer::
+finish_collect(bool format_only) {
+  int num_adjusted = 0;
+
+  NewCollectedList::iterator nci;
+  for (nci = _new_collected_list.begin(); 
+       nci != _new_collected_list.end();
+       ++nci) {
+    NewCollectedData *ncd = (*nci);
+    if (format_only) {
+      num_adjusted += ncd->apply_format_only_changes();
+    } else {
+      num_adjusted += ncd->apply_collect_changes();
+    }
+    delete ncd;
+  }
+
+  _new_collected_list.clear();
+  _new_collected_map.clear();
+  _already_collected_map.clear();
+
+  return num_adjusted;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::premunge_geom
+//       Access: Public
+//  Description: Uses the indicated munger to premunge the given Geom
+//               to optimize it for eventual rendering.  See
+//               SceneGraphReducer::premunge().
+////////////////////////////////////////////////////////////////////
+PT(Geom) GeomTransformer::
+premunge_geom(const Geom *geom, GeomMunger *munger) {
+  // This method had been originally provided to cache the result for
+  // a particular geom/munger and vdata/munger combination, similar to
+  // the way other GeomTransformer methods work.  On reflection, this
+  // additional caching is not necessary, since the GeomVertexFormat
+  // does its own caching, and there's no danger of that cache filling
+  // up during the span of one frame.
+
+  CPT(GeomVertexData) vdata = geom->get_vertex_data();
+  vdata = munger->premunge_data(vdata);
+  CPT(Geom) pgeom = geom;
+  munger->premunge_geom(pgeom, vdata);
+
+  PT(Geom) geom_copy = geom->make_copy();
+  geom_copy->set_vertex_data(vdata);
+
+  return geom_copy;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::NewCollectedData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+GeomTransformer::NewCollectedData::
+NewCollectedData(const GeomVertexData *source_data) {
+  _new_format = source_data->get_format();
+  _vdata_name = source_data->get_name();
+  _usage_hint = source_data->get_usage_hint();
+  _num_vertices = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::NewCollectedData::apply_format_only_changes
+//       Access: Public
+//  Description: Actually adjusts the GeomVertexDatas found in a
+//               collect_vertex_data() format-only call to have the
+//               same vertex format.  Returns the number of vdatas
+//               modified.
+////////////////////////////////////////////////////////////////////
+int GeomTransformer::NewCollectedData::
+apply_format_only_changes() {
+  int num_modified = 0;
+
+  // We probably don't need to use a map, since
+  // GeomVertexData::convert_to() already caches its result, but we do
+  // it anyway just in case there's danger of overflowing the cache.
+  // What the heck, it's easy to do.
+  typedef pmap< CPT(GeomVertexData), CPT(GeomVertexData) > VDataMap;
+  VDataMap vdata_map;
+
+  SourceGeoms::iterator sgi;
+  for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) {
+    SourceGeom &sg = (*sgi);
+    CPT(GeomVertexData) orig_data = sg._geom->get_vertex_data();
+
+    if (orig_data->get_format() != _new_format) {
+      VDataMap::iterator mi = vdata_map.find(orig_data);
+      if (mi != vdata_map.end()) {
+        // Already modified this vdata.
+        sg._geom->set_vertex_data((*mi).second);
+
+      } else {
+        // Modify this vdata to the new format.
+        CPT(GeomVertexData) new_data = orig_data->convert_to(_new_format);
+        vdata_map[orig_data] = new_data;
+        ++num_modified;
+
+        sg._geom->set_vertex_data(new_data);
+      }
+    }
+  }
+
+  return num_modified;
+}
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::NewCollectedData::apply_collect_changes
+//       Access: Public
+//  Description: Actually combines all of the vertex datas found in a
+//               previous call to collect_vertex_data().
+////////////////////////////////////////////////////////////////////
+int GeomTransformer::NewCollectedData::
+apply_collect_changes() {
+  if (_num_vertices == 0) {
+    return 0;
+  }
+
+  _new_data =
+    new GeomVertexData(_vdata_name, _new_format, _usage_hint);
+
+  _new_data->unclean_set_num_rows(_num_vertices);
+
+  // Copy each source data into the new GeomVertexData, one at a time.
+  int vertex_offset = 0;
+  SourceDatas::iterator sdi;
+  for (sdi = _source_datas.begin(); sdi != _source_datas.end(); ++sdi) {
+    SourceData &sd = (*sdi);
+    CPT(GeomVertexData) vdata = sd._vdata;
+
+    if (_new_format != vdata->get_format()) {
+      // Convert (non-destructively) the current Geom's vertex
+      // data to the new format, so we can just blindly append the
+      // vertices to _new_data, within append_vdata().
+      vdata = vdata->convert_to(_new_format);
+    }
+
+    append_vdata(vdata, vertex_offset);
+    vertex_offset += sd._num_vertices;
+  }
+
+  nassertr(vertex_offset == _num_vertices, 0);
+
+  if (_new_btable != (TransformBlendTable *)NULL) {
+    _new_btable->set_rows(_new_btable_rows);
+    _new_data->set_transform_blend_table(_new_btable);
+  }
+
+  update_geoms();
+
+  _new_data.clear();
+  _new_btable.clear();
+  _new_btable_rows.clear();
+
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::NewCollectedData::append_vdata
+//       Access: Public
+//  Description: Appends the vertices from the indicated source
+//               GeomVertexData to the end of the working data.
+////////////////////////////////////////////////////////////////////
+void GeomTransformer::NewCollectedData::
+append_vdata(const GeomVertexData *vdata, int vertex_offset) {
   for (int i = 0; i < vdata->get_num_arrays(); ++i) {
-    PT(GeomVertexArrayData) new_array = new_data->modify_array(i);
+    PT(GeomVertexArrayData) new_array = _new_data->modify_array(i);
     CPT(GeomVertexArrayData) old_array = vdata->get_array(i);
-    int stride = format->get_array(i)->get_stride();
-    int start_byte = offset * stride;
+    int stride = _new_format->get_array(i)->get_stride();
+    int start_byte = vertex_offset * stride;
     int copy_bytes = old_array->get_data_size_bytes();
-    nassertr(start_byte + copy_bytes == new_array->get_data_size_bytes(), 0);
-
+    nassertv(start_byte + copy_bytes <= new_array->get_data_size_bytes());
+    
     new_array->modify_handle()->copy_subdata_from
       (start_byte, copy_bytes, 
        old_array->get_handle(), 0, copy_bytes);
@@ -684,10 +936,9 @@ collect_vertex_data(Geom *geom, int collect_bits) {
   // remapping transform indices in the vertices.  Each of these has a
   // slightly different way to handle the remapping, because they have
   // slightly different kinds of data.
-  typedef vector_int IndexMap;
-
+  
   if (vdata->get_transform_table() != (TransformTable *)NULL ||
-      new_data->get_transform_table() != (TransformTable *)NULL) {
+      _new_data->get_transform_table() != (TransformTable *)NULL) {
     // The TransformTable.
     CPT(TransformTable) old_table;
     if (vdata->get_transform_table() != (TransformTable *)NULL) {
@@ -706,27 +957,27 @@ collect_vertex_data(Geom *geom, int collect_bits) {
     // store an index).
     typedef pmap<const VertexTransform *, int> AddedTransforms;
     AddedTransforms added_transforms;
-
+    
     int num_old_transforms = old_table->get_num_transforms();
     for (int i = 0; i < num_old_transforms; i++) {
       added_transforms[old_table->get_transform(i)] = i;
     }
-
+    
     // Now create a new table.  We have to create a new table instead
     // of modifying the existing one, since a registered
     // TransformTable cannot be modified.
     PT(TransformTable) new_table;
-    if (new_data->get_transform_table() != (TransformTable *)NULL) {
-      new_table = new TransformTable(*new_data->get_transform_table());
+    if (_new_data->get_transform_table() != (TransformTable *)NULL) {
+      new_table = new TransformTable(*_new_data->get_transform_table());
     } else {
       new_table = new TransformTable;
     }
-
+    
     // Now walk through the old table and copy over its transforms.
     // We will build up an IndexMap of old index numbers to new index
     // numbers while we go, which we can use to modify the vertices.
     IndexMap transform_map;
-
+    
     int num_transforms = old_table->get_num_transforms();
     transform_map.reserve(num_transforms);
     for (int ti = 0; ti < num_transforms; ++ti) {
@@ -742,22 +993,23 @@ collect_vertex_data(Geom *geom, int collect_bits) {
         added_transforms[transform] = tj;
       }
     }
-    new_data->set_transform_table(TransformTable::register_table(new_table));
+    _new_data->set_transform_table(TransformTable::register_table(new_table));
 
     // And now modify the vertices to update the indices to their new
     // values in the new table.  This requires a nested loop, since
     // each column of transform_index might define multiple index
     // values.
-    GeomVertexRewriter index(new_data, InternalName::get_transform_index());
+    GeomVertexRewriter index(_new_data, InternalName::get_transform_index());
     if (index.has_column()) {
       int num_values = index.get_column()->get_num_values();
+      int num_rows = vdata->get_num_rows();
       int new_index[4];
-
-      index.set_row(offset);
-      while (!index.is_at_end()) {
+      
+      index.set_row(vertex_offset);
+      for (int ci = 0; ci < num_rows; ++ci) {
         const int *orig_index = index.get_data4i();
         for (int i = 0; i < num_values; i++) {
-          nassertr(orig_index[i] >= 0 && orig_index[i] < (int)transform_map.size(), 0);
+          nassertv(orig_index[i] >= 0 && orig_index[i] < (int)transform_map.size());
           new_index[i] = transform_map[orig_index[i]];
         }
         index.set_data4i(new_index);
@@ -765,35 +1017,25 @@ collect_vertex_data(Geom *geom, int collect_bits) {
     }
   }
 
-  if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL ||
-      new_data->get_transform_blend_table() != (TransformBlendTable *)NULL) {
+  if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL) {
     // The TransformBlendTable.  This one is the easiest, because we
     // can modify it directly, and it will uniquify blend objects for
     // us.
 
-    CPT(TransformBlendTable) old_btable;
-    if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL) {
-      old_btable = vdata->get_transform_blend_table();
-    } else {
-      PT(TransformBlendTable) temp_btable;
-      temp_btable = new TransformBlendTable;
-      temp_btable->add_blend(TransformBlend());
-      old_btable = temp_btable;
-    }
-
-    PT(TransformBlendTable) new_btable;
-    if (new_data->get_transform_blend_table() != (TransformBlendTable *)NULL) {
-      new_btable = new_data->modify_transform_blend_table();
-    } else {
-      new_btable = new TransformBlendTable;
-      new_btable->add_blend(TransformBlend());
-      new_data->set_transform_blend_table(new_btable);
+    // We have few special optimizations to handle the
+    // TransformBlendTable, since it's a very common case and
+    // therefore worth spending a bit of effort to optimize deeply.
+    
+    CPT(TransformBlendTable) old_btable = vdata->get_transform_blend_table();
+    
+    if (_new_btable == (TransformBlendTable *)NULL) {
+      _new_btable = new TransformBlendTable;
+      _new_btable->add_blend(TransformBlend());
     }
 
     SparseArray new_rows = old_btable->get_rows();
-    new_rows <<= offset;
-    new_rows |= new_btable->get_rows();
-    new_btable->set_rows(new_rows);
+    new_rows <<= vertex_offset;
+    _new_btable_rows |= new_rows;
 
     // We still need to build up the IndexMap.
     IndexMap blend_map;
@@ -801,25 +1043,27 @@ collect_vertex_data(Geom *geom, int collect_bits) {
     int num_blends = old_btable->get_num_blends();
     blend_map.reserve(num_blends);
     for (int bi = 0; bi < num_blends; ++bi) {
-      int bj = new_btable->add_blend(old_btable->get_blend(bi));
+      int bj = _new_btable->add_blend(old_btable->get_blend(bi));
       blend_map.push_back(bj);
     }
 
     // Modify the indices.  This is simpler than the transform_index,
     // above, because each column of transform_blend may only define
     // one index value.
-    GeomVertexRewriter index(new_data, InternalName::get_transform_blend());
+    GeomVertexRewriter index(_new_data, InternalName::get_transform_blend());
     if (index.has_column()) {
-      index.set_row(offset);
-      while (!index.is_at_end()) {
+      int num_rows = vdata->get_num_rows();
+      index.set_row(vertex_offset);
+
+      for (int ci = 0; ci < num_rows; ++ci) {
         int orig_index = index.get_data1i();
-        nassertr(orig_index >= 0 && orig_index < (int)blend_map.size(), 0);
+        nassertv(orig_index >= 0 && orig_index < (int)blend_map.size());
         int new_index = blend_map[orig_index];
         index.set_data1i(new_index);
       }
     }
   }
-
+  
   if (vdata->get_slider_table() != (SliderTable *)NULL) {
     // The SliderTable.  This one requires making a copy, like the
     // TransformTable (since it can't be modified once registered
@@ -828,95 +1072,32 @@ collect_vertex_data(Geom *geom, int collect_bits) {
     // an IndexMap to modify the vertices with.
     const SliderTable *old_sliders = vdata->get_slider_table();
     PT(SliderTable) new_sliders;
-    if (new_data->get_slider_table() != (SliderTable *)NULL) {
-      new_sliders = new SliderTable(*new_data->get_slider_table());
+    if (_new_data->get_slider_table() != (SliderTable *)NULL) {
+      new_sliders = new SliderTable(*_new_data->get_slider_table());
     } else {
       new_sliders = new SliderTable;
     }
     int num_sliders = old_sliders->get_num_sliders();
     for (int si = 0; si < num_sliders; ++si) {
       SparseArray new_rows = old_sliders->get_slider_rows(si);
-      new_rows <<= offset;
+      new_rows <<= vertex_offset;
       new_sliders->add_slider(old_sliders->get_slider(si), new_rows);
     }
-    new_data->set_slider_table(SliderTable::register_table(new_sliders));
+    _new_data->set_slider_table(SliderTable::register_table(new_sliders));
   }
-
-  AlreadyCollectedData &acd = _already_collected[vdata];
-  acd._data = new_data;
-  acd._offset = offset;
-  geom->offset_vertices(new_data, offset);
-
-  return num_created;
 }
 
-
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomTransformer::collect_vertex_data
+//     Function: GeomTransformer::NewCollectedData::update_geoms
 //       Access: Public
-//  Description: Collects together individual GeomVertexData
-//               structures that share the same format into one big
-//               GeomVertexData structure.  This is intended to
-//               minimize context switches on the graphics card.
+//  Description: Updates all of the source Geoms to reference the new
+//               vertex data.
 ////////////////////////////////////////////////////////////////////
-int GeomTransformer::
-collect_vertex_data(GeomNode *node, int collect_bits) {
-  int num_created = 0;
-
-  GeomTransformer *dynamic = NULL;
-
-  GeomNode::CDWriter cdata(node->_cycler);
-  GeomNode::GeomList::iterator gi;
-  GeomNode::GeomList &geoms = *(cdata->modify_geoms());
-  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
-    GeomNode::GeomEntry &entry = (*gi);
-    PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
-    entry._geom = new_geom;
-
-    if ((collect_bits & SceneGraphReducer::CVD_avoid_dynamic) != 0 &&
-        new_geom->get_vertex_data()->get_usage_hint() < Geom::UH_static) {
-      // This one has some dynamic properties.  Collect it
-      // independently of the outside world.
-      if (dynamic == (GeomTransformer *)NULL) {
-        dynamic = new GeomTransformer(*this);
-      }
-      num_created += dynamic->collect_vertex_data(new_geom, collect_bits);
-      
-    } else {
-      num_created += collect_vertex_data(new_geom, collect_bits);
-    }
-  }
-
-  if (dynamic != (GeomTransformer *)NULL) {
-    delete dynamic;
+void GeomTransformer::NewCollectedData::
+update_geoms() {
+  SourceGeoms::iterator sgi;
+  for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) {
+    SourceGeom &sg = (*sgi);
+    sg._geom->offset_vertices(_new_data, sg._vertex_offset);
   }
-
-  return num_created;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomTransformer::premunge_geom
-//       Access: Public
-//  Description: Uses the indicated munger to premunge the given Geom
-//               to optimize it for eventual rendering.  See
-//               SceneGraphReducer::premunge().
-////////////////////////////////////////////////////////////////////
-PT(Geom) GeomTransformer::
-premunge_geom(const Geom *geom, GeomMunger *munger) {
-  // This method had been originally provided to cache the result for
-  // a particular geom/munger and vdata/munger combination, similar to
-  // the way other GeomTransformer methods work.  On reflection, this
-  // additional caching is not necessary, since the GeomVertexFormat
-  // does its own caching, and there's no danger of that cache filling
-  // up during the span of one frame.
-
-  CPT(GeomVertexData) vdata = geom->get_vertex_data();
-  vdata = munger->premunge_data(vdata);
-  CPT(Geom) pgeom = geom;
-  munger->premunge_geom(pgeom, vdata);
-
-  PT(Geom) geom_copy = geom->make_copy();
-  geom_copy->set_vertex_data(vdata);
-
-  return geom_copy;
 }

+ 72 - 12
panda/src/pgraph/geomTransformer.h

@@ -79,8 +79,9 @@ public:
   bool doubleside(GeomNode *node);
   bool reverse(GeomNode *node);
 
-  int collect_vertex_data(Geom *geom, int collect_bits);
-  int collect_vertex_data(GeomNode *node, int collect_bits);
+  int collect_vertex_data(Geom *geom, int collect_bits, bool format_only);
+  int collect_vertex_data(GeomNode *node, int collect_bits, bool format_only);
+  int finish_collect(bool format_only);
 
   PT(Geom) premunge_geom(const Geom *geom, GeomMunger *munger);
 
@@ -138,14 +139,6 @@ private:
   typedef pmap<CPT(GeomVertexData), CPT(GeomVertexData) > ReversedNormals;
   ReversedNormals _reversed_normals;
 
-  class AlreadyCollectedData {
-  public:
-    CPT(GeomVertexData) _data;
-    int _offset;
-  };
-  typedef pmap< CPT(GeomVertexData), AlreadyCollectedData> AlreadyCollected;
-  AlreadyCollected _already_collected;
-
   class NewCollectedKey {
   public:
     INLINE bool operator < (const NewCollectedKey &other) const;
@@ -155,14 +148,81 @@ private:
     Geom::UsageHint _usage_hint;
     Geom::AnimationType _animation_type;
   };
-  typedef pmap< NewCollectedKey, PT(GeomVertexData) > NewCollectedData;
-  NewCollectedData _new_collected_data;
+
+  class SourceData {
+  public:
+    const GeomVertexData *_vdata;
+    int _num_vertices;
+  };
+  typedef pvector<SourceData> SourceDatas;
+  class SourceGeom {
+  public:
+    Geom *_geom;
+    int _vertex_offset;
+  };
+  typedef pvector<SourceGeom> SourceGeoms;
+  class NewCollectedData {
+  public:
+    ALLOC_DELETED_CHAIN(NewCollectedData);
+
+    NewCollectedData(const GeomVertexData *source_data);
+    void add_source_data(const GeomVertexData *source_data);
+    int apply_format_only_changes();
+    int apply_collect_changes();
+
+    CPT(GeomVertexFormat) _new_format;
+    string _vdata_name;
+    GeomEnums::UsageHint _usage_hint;
+    SourceDatas _source_datas;
+    SourceGeoms _source_geoms;
+    int _num_vertices;
+
+  private:
+    // These are used just during apply_changes().
+    void append_vdata(const GeomVertexData *vdata, int vertex_offset);
+    void update_geoms();
+
+    typedef vector_int IndexMap;
+
+    PT(GeomVertexData) _new_data;
+    PT(TransformBlendTable) _new_btable;
+    SparseArray _new_btable_rows;
+
+    // We need a TypeHandle just for ALLOC_DELETED_CHAIN.
+  public:
+    static TypeHandle get_class_type() {
+      return _type_handle;
+    }
+    static void init_type() {
+      register_type(_type_handle, "GeomTransformer::NewCollectedData");
+    }
+    
+  private:
+    static TypeHandle _type_handle;
+  };
+  typedef pvector<NewCollectedData *> NewCollectedList;
+  typedef pmap<NewCollectedKey, NewCollectedData *> NewCollectedMap;
+  NewCollectedList _new_collected_list;
+  NewCollectedMap _new_collected_map;
+
+  class AlreadyCollectedData {
+  public:
+    NewCollectedData *_ncd;
+    int _vertex_offset;
+  };
+  typedef pmap<CPT(GeomVertexData), AlreadyCollectedData> AlreadyCollectedMap;
+  AlreadyCollectedMap _already_collected_map;
 
   static PStatCollector _apply_vertex_collector;
   static PStatCollector _apply_texcoord_collector;
   static PStatCollector _apply_set_color_collector;
   static PStatCollector _apply_scale_color_collector;
   static PStatCollector _apply_set_format_collector;
+    
+public:
+  static void init_type() {
+    NewCollectedData::init_type();
+  }
 };
 
 #include "geomTransformer.I"

+ 10 - 0
panda/src/pgraph/sceneGraphAnalyzer.I

@@ -117,6 +117,16 @@ get_num_geom_vertex_datas() const {
   return _num_geom_vertex_datas;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphAnalyzer::get_num_geom_vertex_formats
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int SceneGraphAnalyzer::
+get_num_geom_vertex_formats() const {
+  return _num_geom_vertex_formats;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphAnalyzer::get_vertex_data_size
 //       Access: Published

+ 12 - 1
panda/src/pgraph/sceneGraphAnalyzer.cxx

@@ -65,6 +65,7 @@ void SceneGraphAnalyzer::
 clear() {
   _nodes.clear();
   _vdatas.clear();
+  _vformats.clear();
   _vadatas.clear();
   _unique_vdatas.clear();
   _unique_vadatas.clear();
@@ -78,6 +79,7 @@ clear() {
   _num_geom_nodes = 0;
   _num_geoms = 0;
   _num_geom_vertex_datas = 0;
+  _num_geom_vertex_formats = 0;
   _vertex_data_size = 0;
   _prim_data_size = 0;
 
@@ -138,7 +140,9 @@ write(ostream &out, int indent_level) const {
 
   indent(out, indent_level)
     << _num_geoms << " Geoms, with " << _num_geom_vertex_datas 
-    << " GeomVertexDatas, appear on " << _num_geom_nodes << " GeomNodes.\n";
+    << " GeomVertexDatas and " << _num_geom_vertex_formats 
+    << " GeomVertexFormats, appear on " << _num_geom_nodes
+    << " GeomNodes.\n";
 
   indent(out, indent_level)
     << _num_vertices << " vertices, " << _num_normals << " normals, "
@@ -373,6 +377,13 @@ collect_statistics(const Geom *geom) {
   if (result.second) {
     // This is the first time we've encountered this vertex data.
     ++_num_geom_vertex_datas;
+    
+    CPT(GeomVertexFormat) vformat = vdata->get_format();
+    bool format_inserted = _vformats.insert(vformat).second;
+    if (format_inserted) {
+      // This is the first time we've encountered this vertex format.
+      ++_num_geom_vertex_formats;
+    }
 
     int &dup_count = (*(_unique_vdatas.insert(UniqueVDatas::value_type(vdata, 0)).first)).second;
     ++dup_count;

+ 4 - 0
panda/src/pgraph/sceneGraphAnalyzer.h

@@ -67,6 +67,7 @@ PUBLISHED:
   INLINE int get_num_geom_nodes() const;
   INLINE int get_num_geoms() const;
   INLINE int get_num_geom_vertex_datas() const;
+  INLINE int get_num_geom_vertex_formats() const;
   INLINE int get_vertex_data_size() const;
 
   INLINE int get_num_vertices() const;
@@ -104,6 +105,7 @@ private:
 
   typedef pmap<PandaNode *, int> Nodes;
   typedef pmap<CPT(GeomVertexData), VDataTracker> VDatas;
+  typedef pset<CPT(GeomVertexFormat) > VFormats;
   typedef pset<CPT(GeomVertexArrayData) > VADatas;
   typedef pmap<const GeomVertexData *, int, IndirectCompareTo<GeomVertexData> > UniqueVDatas;
   typedef pmap<const GeomVertexArrayData *, int, IndirectCompareTo<GeomVertexArrayData> > UniqueVADatas;
@@ -113,6 +115,7 @@ private:
 
   Nodes _nodes;
   VDatas _vdatas;
+  VFormats _vformats;
   VADatas _vadatas;
   VADatas _prim_vadatas;
   UniqueVDatas _unique_vdatas;
@@ -129,6 +132,7 @@ private:
   int _num_geom_nodes;
   int _num_geoms;
   int _num_geom_vertex_datas;
+  int _num_geom_vertex_formats;
   size_t _vertex_data_size;
   size_t _prim_data_size;
 

+ 35 - 1
panda/src/pgraph/sceneGraphReducer.I

@@ -121,6 +121,37 @@ apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
   r_apply_attribs(node, attribs, attrib_types, transformer);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::make_compatible_format
+//       Access: Published
+//  Description: Walks through the tree at this node and below and
+//               unifies the GeomVertexFormat for any GeomVertexData
+//               objects that are found, so that all eligible vdatas
+//               (according to collect_bits; see collect_vertex_data)
+//               will share the same vertex format.
+//
+//               This will add unused columns where necessary to match
+//               formats.  It can result in suboptimal performance if
+//               used needlessly.
+//
+//               There is usually no reason to call this explicitly,
+//               since collect_vertex_data() will do this anyway if it
+//               has not been done already.  However, calling it ahead
+//               of time can make that future call to
+//               collect_vertex_data() run a little bit faster.
+//
+//               The return value is the number of vertex datas
+//               modified.
+////////////////////////////////////////////////////////////////////
+INLINE int SceneGraphReducer::
+make_compatible_format(PandaNode *root, int collect_bits) {
+  PStatTimer timer(_collect_collector);
+  int count = 0;
+  count += r_collect_vertex_data(root, collect_bits, _transformer, true);
+  count += _transformer.finish_collect(true);
+  return count;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::collect_vertex_data
 //       Access: Published
@@ -140,7 +171,10 @@ apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
 INLINE int SceneGraphReducer::
 collect_vertex_data(PandaNode *root, int collect_bits) {
   PStatTimer timer(_collect_collector);
-  return r_collect_vertex_data(root, collect_bits, _transformer);
+  int count = 0;
+  count += r_collect_vertex_data(root, collect_bits, _transformer, false);
+  count += _transformer.finish_collect(false);
+  return count;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 11 - 13
panda/src/pgraph/sceneGraphReducer.cxx

@@ -726,8 +726,8 @@ r_remove_column(PandaNode *node, const InternalName *column,
 ////////////////////////////////////////////////////////////////////
 int SceneGraphReducer::
 r_collect_vertex_data(PandaNode *node, int collect_bits,
-                      GeomTransformer &transformer) {
-  int num_created = 0;
+                      GeomTransformer &transformer, bool format_only) {
+  int num_adjusted = 0;
 
   int this_node_bits = 0;
   if (node->is_of_type(ModelNode::get_class_type())) {
@@ -746,37 +746,35 @@ r_collect_vertex_data(PandaNode *node, int collect_bits,
 
     if (node->is_geom_node()) {
       // When we come to a geom node, collect.
-      if (new_transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits)) {
-        ++num_created;
-      }
+      num_adjusted += new_transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits, format_only);
     }
 
     PandaNode::Children children = node->get_children();
     int num_children = children.get_num_children();
     for (int i = 0; i < num_children; ++i) {
-      num_created += 
-        r_collect_vertex_data(children.get_child(i), collect_bits, new_transformer);
+      num_adjusted += 
+        r_collect_vertex_data(children.get_child(i), collect_bits, new_transformer, format_only);
     }
 
+    num_adjusted += new_transformer.finish_collect(format_only);
+
   } else {
     // Keep the same collection.
 
     if (node->is_geom_node()) {
-      if (transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits)) {
-        ++num_created;
-      }
+      num_adjusted += transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits, format_only);
     }
     
     PandaNode::Children children = node->get_children();
     int num_children = children.get_num_children();
     for (int i = 0; i < num_children; ++i) {
-      num_created +=
-        r_collect_vertex_data(children.get_child(i), collect_bits, transformer);
+      num_adjusted +=
+        r_collect_vertex_data(children.get_child(i), collect_bits, transformer, format_only);
     }
   }
 
   Thread::consider_yield();
-  return num_created;
+  return num_adjusted;
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -138,6 +138,7 @@ PUBLISHED:
 
   int remove_column(PandaNode *root, const InternalName *column);
 
+  INLINE int make_compatible_format(PandaNode *root, int collect_bits = ~0);
   INLINE int collect_vertex_data(PandaNode *root, int collect_bits = ~0);
   INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0);
   void unify(PandaNode *root, bool preserve_order);
@@ -173,7 +174,7 @@ protected:
                       GeomTransformer &transformer);
 
   int r_collect_vertex_data(PandaNode *node, int collect_bits,
-                            GeomTransformer &transformer);
+                            GeomTransformer &transformer, bool format_only);
   int r_make_nonindexed(PandaNode *node, int collect_bits);
   void r_unify(PandaNode *node, int max_indices, bool preserve_order);