Browse Source

define NodePath::premunge_scene() and supporting implementation

David Rose 18 years ago
parent
commit
732bbafaa9

+ 49 - 2
panda/src/display/standardMunger.cxx

@@ -170,8 +170,55 @@ munge_data_impl(const GeomVertexData *data) {
 //  Description: Converts a Geom and/or its data as necessary.
 //  Description: Converts a Geom and/or its data as necessary.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool StandardMunger::
 bool StandardMunger::
-munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data,
-                Thread *current_thread) {
+munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) {
+  int supported_geom_rendering = get_gsg()->get_supported_geom_rendering();
+
+  int unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering;
+  if (unsupported_bits != 0) {
+    // Even beyond munging the vertex format, we have to convert the
+    // Geom itself into a new primitive type the GSG can render
+    // directly.
+    if ((unsupported_bits & Geom::GR_composite_bits) != 0) {
+      // This decomposes everything in the primitive, so that if (for
+      // instance) the primitive contained both strips and fans, but
+      // the GSG didn't support fans, it would decompose the strips
+      // too.  To handle this correctly, we'd need a separate
+      // decompose_fans() and decompose_strips() call; but for now,
+      // we'll just say it's good enough.  In practice, we don't have
+      // any GSG's that can support strips without also supporting
+      // fans.
+      geom = geom->decompose();
+
+      // Decomposing might produce an indexed Geom, so re-check the
+      // unsupported bits.
+      unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering;
+    }
+    if ((unsupported_bits & Geom::GR_shade_model_bits) != 0) {
+      // Rotate the vertices to account for different shade-model
+      // expectations (e.g. SM_flat_last_vertex to
+      // SM_flat_first_vertex)
+      geom = geom->rotate();
+    }
+    if ((unsupported_bits & Geom::GR_indexed_bits) != 0) {
+      // Convert indexed geometry to nonindexed geometry.
+      PT(Geom) new_geom = geom->make_copy();
+      new_geom->set_vertex_data(vertex_data);
+      new_geom->make_nonindexed(false);
+      geom = new_geom;
+      vertex_data = new_geom->get_vertex_data();
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StandardMunger::premunge_geom_impl
+//       Access: Protected, Virtual
+//  Description: Converts a Geom and/or its data as necessary.
+////////////////////////////////////////////////////////////////////
+bool StandardMunger::
+premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
   int supported_geom_rendering = get_gsg()->get_supported_geom_rendering();
   int supported_geom_rendering = get_gsg()->get_supported_geom_rendering();
 
 
   int unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering;
   int unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering;

+ 1 - 0
panda/src/display/standardMunger.h

@@ -49,6 +49,7 @@ protected:
   virtual int compare_to_impl(const GeomMunger *other) const;
   virtual int compare_to_impl(const GeomMunger *other) const;
   virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
   virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
                                Thread *current_thread);
                                Thread *current_thread);
+  virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data);
   virtual int geom_compare_to_impl(const GeomMunger *other) const;
   virtual int geom_compare_to_impl(const GeomMunger *other) const;
   virtual CPT(RenderState) munge_state_impl(const RenderState *state);
   virtual CPT(RenderState) munge_state_impl(const RenderState *state);
 
 

+ 0 - 17
panda/src/glstuff/glGeomMunger_src.I

@@ -16,20 +16,3 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: CLP(GeomMunger)::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE CLP(GeomMunger)::
-CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
-  StandardMunger(gsg, state, 4, NT_uint8, C_color),
-  _texture(state->get_texture()),
-  _tex_gen(state->get_tex_gen())
-{
-  // Set a callback to unregister ourselves when either the Texture or
-  // the TexGen object gets deleted.
-  _texture.set_callback(this);
-  _tex_gen.set_callback(this);
-}

+ 236 - 11
panda/src/glstuff/glGeomMunger_src.cxx

@@ -21,6 +21,31 @@
 GeomMunger *CLP(GeomMunger)::_deleted_chain = NULL;
 GeomMunger *CLP(GeomMunger)::_deleted_chain = NULL;
 TypeHandle CLP(GeomMunger)::_type_handle;
 TypeHandle CLP(GeomMunger)::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(GeomMunger)::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CLP(GeomMunger)::
+CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
+  StandardMunger(gsg, state, 4, NT_uint8, C_color),
+  _texture(state->get_texture()),
+  _tex_gen(state->get_tex_gen())
+{
+  // Set a callback to unregister ourselves when either the Texture or
+  // the TexGen object gets deleted.
+  _texture.set_callback(this);
+  _tex_gen.set_callback(this);
+
+  _flags = 0;
+
+  if (CLP(interleaved_arrays)) {
+    _flags |= F_interleaved_arrays;
+  } else if (CLP(parallel_arrays)) {
+    _flags |= F_parallel_arrays;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GeomMunger)::Destructor
 //     Function: CLP(GeomMunger)::Destructor
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -113,22 +138,211 @@ munge_format_impl(const GeomVertexFormat *orig,
     }
     }
   }
   }
 
 
-  /*
-  if (true) {
+  CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(new_format);
+
+  if ((_flags & F_parallel_arrays) != 0) {
+    // Split out the interleaved array into n parallel arrays.
+    new_format = new GeomVertexFormat;
+    for (int i = 0; i < format->get_num_columns(); ++i) {
+      const GeomVertexColumn *column = format->get_column(i);
+      PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
+      new_array_format->add_column(column->get_name(), column->get_num_components(),
+                                   column->get_numeric_type(), column->get_contents());
+      new_format->add_array(new_array_format);
+    }
+    format = GeomVertexFormat::register_format(new_format);
+  
+  } else if ((_flags & F_interleaved_arrays) != 0) {
+    // Combine the primary data columns into a single array.
+    new_format = new GeomVertexFormat(*format);
+    PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
+  
+    const GeomVertexColumn *column = format->get_vertex_column();
+    if (column != (const GeomVertexColumn *)NULL) {
+      new_array_format->add_column
+        (column->get_name(), column->get_num_components(), 
+         column->get_numeric_type(), column->get_contents());
+      new_format->remove_column(column->get_name());
+    }
+
+    column = format->get_normal_column();
+    if (column != (const GeomVertexColumn *)NULL) {
+      new_array_format->add_column
+        (column->get_name(), column->get_num_components(), 
+         column->get_numeric_type(), column->get_contents());
+      new_format->remove_column(column->get_name());
+    }
+
+    column = format->get_color_column();
+    if (column != (const GeomVertexColumn *)NULL) {
+      new_array_format->add_column
+        (column->get_name(), column->get_num_components(), 
+         column->get_numeric_type(), column->get_contents());
+      new_format->remove_column(column->get_name());
+    }
+
+    // Put only the used texture coordinates into the interleaved
+    // array.
+    if (_texture != (TextureAttrib *)NULL) {
+      typedef pset<const InternalName *> UsedStages;
+      UsedStages used_stages;
+      
+      int num_stages = _texture->get_num_on_stages();
+      for (int i = 0; i < num_stages; ++i) {
+        TextureStage *stage = _texture->get_on_stage(i);
+        if (_tex_gen == (TexGenAttrib *)NULL ||
+            !_tex_gen->has_stage(stage)) {
+          InternalName *name = stage->get_texcoord_name();
+          if (used_stages.insert(name).second) {
+            // This is the first time we've encountered this texcoord name.
+            const GeomVertexColumn *texcoord_type = format->get_column(name);
+            
+            if (texcoord_type != (const GeomVertexColumn *)NULL) {
+              new_array_format->add_column
+                (name, texcoord_type->get_num_values(), NT_float32, C_texcoord);
+            } else {
+              // We have to add something as a placeholder, even if the
+              // texture coordinates aren't defined.
+              new_array_format->add_column(name, 2, NT_float32, C_texcoord);
+            }
+            new_format->remove_column(name);
+          }
+        }
+      }
+    }
+
+    new_format->insert_array(0, new_array_format);
+    format = GeomVertexFormat::register_format(new_format);
+  }
+
+  return format;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(GeomMunger)::premunge_format_impl
+//       Access: Protected, Virtual
+//  Description: Given a source GeomVertexFormat, converts it if
+//               necessary to the appropriate format for rendering.
+////////////////////////////////////////////////////////////////////
+CPT(GeomVertexFormat) CLP(GeomMunger)::
+premunge_format_impl(const GeomVertexFormat *orig) {
+  PT(GeomVertexFormat) new_format = new GeomVertexFormat(*orig);
+
+  const GeomVertexColumn *color_type = orig->get_color_column();
+  if (color_type != (GeomVertexColumn *)NULL &&
+      color_type->get_numeric_type() == NT_packed_dabc) {
+    // We need to convert the color format; OpenGL doesn't support the
+    // byte order of DirectX's packed ARGB format.
+    int color_array = orig->get_array_with(InternalName::get_color());
+
+    PT(GeomVertexArrayFormat) new_array_format = new_format->modify_array(color_array);
+
+    // Replace the existing color format with the new format.
+    new_array_format->add_column
+      (InternalName::get_color(), 4, NT_uint8,
+       C_color, color_type->get_start());
+  }
+
+  CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(new_format);
+
+  if ((_flags & F_parallel_arrays) != 0) {
     // Split out the interleaved array into n parallel arrays.
     // Split out the interleaved array into n parallel arrays.
-    CPT(GeomVertexFormat) format = new_format;
     new_format = new GeomVertexFormat;
     new_format = new GeomVertexFormat;
-    for (int i = 0; i < format->get_num_columns(); i++) {
+    for (int i = 0; i < format->get_num_columns(); ++i) {
       const GeomVertexColumn *column = format->get_column(i);
       const GeomVertexColumn *column = format->get_column(i);
       PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
       PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
       new_array_format->add_column(column->get_name(), column->get_num_components(),
       new_array_format->add_column(column->get_name(), column->get_num_components(),
-                                      column->get_numeric_type());
+                                   column->get_numeric_type(), column->get_contents());
       new_format->add_array(new_array_format);
       new_format->add_array(new_array_format);
     }
     }
+    format = GeomVertexFormat::register_format(new_format);
+  
+  } else {
+    // Combine the primary data columns into a single array.  Unlike
+    // the munge case, above, in the premunge case, we do this even if
+    // F_interleaved_arrays is not set (unless F_parallel_arrays is
+    // set), since the presumption is that you're more willing to pay
+    // the overhead of doing this step at load time than you might be
+    // at run time.
+    new_format = new GeomVertexFormat(*format);
+    PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
+  
+    const GeomVertexColumn *column = format->get_vertex_column();
+    if (column != (const GeomVertexColumn *)NULL) {
+      new_array_format->add_column
+        (column->get_name(), column->get_num_components(), 
+         column->get_numeric_type(), column->get_contents());
+      new_format->remove_column(column->get_name());
+    }
+
+    column = format->get_normal_column();
+    if (column != (const GeomVertexColumn *)NULL) {
+      new_array_format->add_column
+        (column->get_name(), column->get_num_components(), 
+         column->get_numeric_type(), column->get_contents());
+      new_format->remove_column(column->get_name());
+    }
+
+    column = format->get_color_column();
+    if (column != (const GeomVertexColumn *)NULL) {
+      new_array_format->add_column
+        (column->get_name(), column->get_num_components(), 
+         column->get_numeric_type(), column->get_contents());
+      new_format->remove_column(column->get_name());
+    }
+
+    // Put only the used texture coordinates into the interleaved
+    // array.  The others will be kept around, but in a parallel
+    // array.
+    if (_texture != (TextureAttrib *)NULL) {
+      typedef pset<const InternalName *> UsedStages;
+      UsedStages used_stages;
+      
+      int num_stages = _texture->get_num_on_stages();
+      for (int i = 0; i < num_stages; ++i) {
+        TextureStage *stage = _texture->get_on_stage(i);
+        if (_tex_gen == (TexGenAttrib *)NULL ||
+            !_tex_gen->has_stage(stage)) {
+          InternalName *name = stage->get_texcoord_name();
+          if (used_stages.insert(name).second) {
+            // This is the first time we've encountered this texcoord name.
+            const GeomVertexColumn *texcoord_type = format->get_column(name);
+            
+            if (texcoord_type != (const GeomVertexColumn *)NULL) {
+              new_array_format->add_column
+                (name, texcoord_type->get_num_values(), NT_float32, C_texcoord);
+            } else {
+              // We have to add something as a placeholder, even if the
+              // texture coordinates aren't defined.
+              new_array_format->add_column(name, 2, NT_float32, C_texcoord);
+            }
+            new_format->remove_column(name);
+          }
+        }
+      }
+    }
+
+    // Now go through the remaining arrays and make sure they are
+    // tightly packed.  If not, repack them.
+    for (int i = 0; i < new_format->get_num_arrays(); ++i) {
+      CPT(GeomVertexArrayFormat) orig_a = new_format->get_array(i);
+      if (orig_a->count_unused_space() != 0) {
+        PT(GeomVertexArrayFormat) new_a = new GeomVertexArrayFormat;
+        for (int j = 0; j < orig_a->get_num_columns(); ++j) {
+          const GeomVertexColumn *column = orig_a->get_column(j);
+          new_a->add_column(column->get_name(), column->get_num_components(),
+                            column->get_numeric_type(), column->get_contents());
+        }
+        new_format->set_array(i, new_a);
+      }
+    }
+
+    // Finally, insert the interleaved array first in the format.
+    new_format->insert_array(0, new_array_format);
+    format = GeomVertexFormat::register_format(new_format);
   }
   }
-  */
 
 
-  return GeomVertexFormat::register_format(new_format);
+  return format;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -148,6 +362,9 @@ compare_to_impl(const GeomMunger *other) const {
   if (_tex_gen != om->_tex_gen) {
   if (_tex_gen != om->_tex_gen) {
     return _tex_gen < om->_tex_gen ? -1 : 1;
     return _tex_gen < om->_tex_gen ? -1 : 1;
   }
   }
+  if (_flags != om->_flags) {
+    return _flags < om->_flags ? -1 : 1;
+  }
 
 
   return StandardMunger::compare_to_impl(other);
   return StandardMunger::compare_to_impl(other);
 }
 }
@@ -162,8 +379,16 @@ compare_to_impl(const GeomMunger *other) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int CLP(GeomMunger)::
 int CLP(GeomMunger)::
 geom_compare_to_impl(const GeomMunger *other) const {
 geom_compare_to_impl(const GeomMunger *other) const {
-  // We don't consider _texture and _tex_gen for this purpose; they
-  // affect only whether the GL display list should be regenerated or
-  // not, and don't require reconverting the vertices.
-  return StandardMunger::geom_compare_to_impl(other);
+  const CLP(GeomMunger) *om = DCAST(CLP(GeomMunger), other);
+  if (_texture != om->_texture) {
+    return _texture < om->_texture ? -1 : 1;
+  }
+  if (_tex_gen != om->_tex_gen) {
+    return _tex_gen < om->_tex_gen ? -1 : 1;
+  }
+  if (_flags != om->_flags) {
+    return _flags < om->_flags ? -1 : 1;
+  }
+
+  return StandardMunger::compare_to_impl(other);
 }
 }

+ 10 - 2
panda/src/glstuff/glGeomMunger_src.h

@@ -35,7 +35,7 @@ class CLP(GeomContext);
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_GL CLP(GeomMunger) : public StandardMunger, public WeakPointerCallback {
 class EXPCL_GL CLP(GeomMunger) : public StandardMunger, public WeakPointerCallback {
 public:
 public:
-  INLINE CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state);
+  CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state);
   virtual ~CLP(GeomMunger)();
   virtual ~CLP(GeomMunger)();
   ALLOC_DELETED_CHAIN(CLP(GeomMunger));
   ALLOC_DELETED_CHAIN(CLP(GeomMunger));
 
 
@@ -43,7 +43,9 @@ public:
 
 
 protected:
 protected:
   virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig,
   virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig,
-                                                    const GeomVertexAnimationSpec &animation);
+                                                  const GeomVertexAnimationSpec &animation);
+  virtual CPT(GeomVertexFormat) premunge_format_impl(const GeomVertexFormat *orig);
+
   virtual int compare_to_impl(const GeomMunger *other) const;
   virtual int compare_to_impl(const GeomMunger *other) const;
   virtual int geom_compare_to_impl(const GeomMunger *other) const;
   virtual int geom_compare_to_impl(const GeomMunger *other) const;
 
 
@@ -54,6 +56,12 @@ private:
   typedef pset<CLP(GeomContext) *> GeomContexts;
   typedef pset<CLP(GeomContext) *> GeomContexts;
   GeomContexts _geom_contexts;
   GeomContexts _geom_contexts;
 
 
+  enum Flags {
+    F_interleaved_arrays   = 0x0001,
+    F_parallel_arrays      = 0x0002,
+  };
+  int _flags;
+
   static GeomMunger *_deleted_chain;
   static GeomMunger *_deleted_chain;
 
 
 public:
 public:

+ 17 - 0
panda/src/glstuff/glmisc_src.cxx

@@ -79,6 +79,23 @@ ConfigVariableBool CLP(compile_and_execute)
             "for the first time, by allowing the display list to be "
             "for the first time, by allowing the display list to be "
             "rendered at the same time it is being compiled."));
             "rendered at the same time it is being compiled."));
 
 
+ConfigVariableBool CLP(interleaved_arrays)
+  ("gl-interleaved-arrays", false,
+   PRC_DESC("Set this true to convert OpenGL geometry such that the "
+            "primary data columns vertex, normal, color, and texcoord "
+            "are interleaved into one array when possible, or false to "
+            "render geometry as it appears in the GeomVertexData.  See "
+            "also gl-parallel-arrays."));
+
+ConfigVariableBool CLP(parallel_arrays)
+  ("gl-parallel-arrays", false,
+   PRC_DESC("Set this true to convert OpenGL geometry such that each "
+            "data column is a separate array, or false to "
+            "render geometry as it appears in the GeomVertexData.  See "
+            "also gl-interleaved-arrays."));
+
+extern ConfigVariableBool CLP(parallel_arrays);
+
 void CLP(init_classes)() {
 void CLP(init_classes)() {
   CLP(GeomContext)::init_type();
   CLP(GeomContext)::init_type();
   CLP(GeomMunger)::init_type();
   CLP(GeomMunger)::init_type();

+ 2 - 0
panda/src/glstuff/glmisc_src.h

@@ -30,6 +30,8 @@ extern ConfigVariableBool CLP(force_mipmaps);
 extern ConfigVariableBool CLP(color_mask);
 extern ConfigVariableBool CLP(color_mask);
 extern ConfigVariableBool CLP(support_occlusion_query);
 extern ConfigVariableBool CLP(support_occlusion_query);
 extern ConfigVariableBool CLP(compile_and_execute);
 extern ConfigVariableBool CLP(compile_and_execute);
+extern ConfigVariableBool CLP(interleaved_arrays);
+extern ConfigVariableBool CLP(parallel_arrays);
 
 
 extern EXPCL_GL void CLP(init_classes)();
 extern EXPCL_GL void CLP(init_classes)();
 
 

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

@@ -59,6 +59,19 @@ get_total_size() const {
   return _total_size;
   return _total_size;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomCacheManager::evict_old_entries
+//       Access: Public
+//  Description: Trims the cache size down to get_max_size() by
+//               evicting old cache entries as needed.  It is assumed
+//               that you already hold the lock before calling this
+//               method.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomCacheManager::
+evict_old_entries() {
+  evict_old_entries(get_max_size(), true);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomCacheManager::flush_level
 //     Function: GeomCacheManager::flush_level
 //       Access: Public, Static
 //       Access: Public, Static

+ 15 - 5
panda/src/gobj/geomCacheManager.cxx

@@ -56,6 +56,17 @@ GeomCacheManager::
   nassertv(false);
   nassertv(false);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomCacheManager::flush
+//       Access: Published
+//  Description: Immediately empties all elements in the cache.
+////////////////////////////////////////////////////////////////////
+void GeomCacheManager::
+flush() {
+  MutexHolder holder(_lock);
+  evict_old_entries(0, false);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomCacheManager::get_global_ptr
 //     Function: GeomCacheManager::get_global_ptr
 //       Access: Published, Static
 //       Access: Published, Static
@@ -71,23 +82,22 @@ get_global_ptr() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomCacheManager::evict_old_entries
 //     Function: GeomCacheManager::evict_old_entries
-//       Access: Private
-//  Description: Trims the cache size down to get_max_size() by
+//       Access: Public
+//  Description: Trims the cache size down to the specified size by
 //               evicting old cache entries as needed.  It is assumed
 //               evicting old cache entries as needed.  It is assumed
 //               that you already hold the lock before calling this
 //               that you already hold the lock before calling this
 //               method.
 //               method.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomCacheManager::
 void GeomCacheManager::
-evict_old_entries() {
+evict_old_entries(int max_size, bool keep_current) {
   int current_frame = ClockObject::get_global_clock()->get_frame_count();
   int current_frame = ClockObject::get_global_clock()->get_frame_count();
   int min_frames = geom_cache_min_frames;
   int min_frames = geom_cache_min_frames;
 
 
-  int max_size = get_max_size();
   while (_total_size > max_size) {
   while (_total_size > max_size) {
     PT(GeomCacheEntry) entry = _list->_next;
     PT(GeomCacheEntry) entry = _list->_next;
     nassertv(entry != _list);
     nassertv(entry != _list);
 
 
-    if (current_frame - entry->_last_frame_used < min_frames) {
+    if (keep_current && current_frame - entry->_last_frame_used < min_frames) {
       // Never mind, this one is too new.
       // Never mind, this one is too new.
       if (gobj_cat.is_debug()) {
       if (gobj_cat.is_debug()) {
         gobj_cat.debug()
         gobj_cat.debug()

+ 4 - 1
panda/src/gobj/geomCacheManager.h

@@ -55,10 +55,13 @@ PUBLISHED:
 
 
   INLINE int get_total_size() const;
   INLINE int get_total_size() const;
 
 
+  void flush();
+
   static GeomCacheManager *get_global_ptr();
   static GeomCacheManager *get_global_ptr();
 
 
 public:
 public:
-  void evict_old_entries();
+  INLINE void evict_old_entries();
+  void evict_old_entries(int max_size, bool keep_current);
   INLINE static void flush_level();
   INLINE static void flush_level();
 
 
 private:
 private:

+ 49 - 0
panda/src/gobj/geomMunger.I

@@ -106,6 +106,55 @@ munge_data(const GeomVertexData *data) const {
   return ((GeomMunger *)this)->munge_data_impl(data);
   return ((GeomMunger *)this)->munge_data_impl(data);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomMunger::premunge_format
+//       Access: Public
+//  Description: This is similar to munge_format(), but it is done at
+//               load time, to optimize a model for eventual rendering
+//               on a particular GSG.  At this point, we do not
+//               necessarily know the final render state that will be
+//               applied, so we cannot make any destructive changes to
+//               the geom, its data, or its format.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(GeomVertexFormat) GeomMunger::
+premunge_format(const GeomVertexFormat *format) const {
+  return ((GeomMunger *)this)->do_premunge_format(format);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomMunger::premunge_data
+//       Access: Public
+//  Description: This is similar to munge_data(), but it is done at
+//               load time, to optimize a model for eventual rendering
+//               on a particular GSG.  At this point, we do not
+//               necessarily know the final render state that will be
+//               applied, so we cannot make any destructive changes to
+//               the geom, its data, or its format.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(GeomVertexData) GeomMunger::
+premunge_data(const GeomVertexData *data) const {
+  return ((GeomMunger *)this)->premunge_data_impl(data);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomMunger::premunge_geom
+//       Access: Public
+//  Description: This is similar to munge_geom(), but it is done at
+//               load time, to optimize a model for eventual rendering
+//               on a particular GSG.  At this point, we do not
+//               necessarily know the final render state that will be
+//               applied, so we cannot make any destructive changes to
+//               the geom, its data, or its format.
+//
+//               Unlike munge_geom(), this result is not cached, since
+//               the assumption is that this operation is performed at
+//               load time once for each model.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomMunger::
+premunge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data) const {
+  ((GeomMunger *)this)->premunge_geom_impl(geom, data);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomMunger::compare_to
 //     Function: GeomMunger::compare_to
 //       Access: Public
 //       Access: Public

+ 77 - 1
panda/src/gobj/geomMunger.cxx

@@ -96,7 +96,7 @@ remove_data(const GeomVertexData *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomMunger::munge_geom
 //     Function: GeomMunger::munge_geom
-//       Access: Published
+//       Access: Public
 //  Description: Applies the indicated munger to the geom and its
 //  Description: Applies the indicated munger to the geom and its
 //               data, and returns a (possibly different) geom and
 //               data, and returns a (possibly different) geom and
 //               data, according to the munger's whim.  
 //               data, according to the munger's whim.  
@@ -262,6 +262,82 @@ munge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &, Thread *) {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomMunger::do_premunge_format
+//       Access: Protected
+//  Description: The protected implementation of premunge_format().  This
+//               exists just to cast away the const pointer.
+////////////////////////////////////////////////////////////////////
+CPT(GeomVertexFormat) GeomMunger::
+do_premunge_format(const GeomVertexFormat *format) {
+  nassertr(_is_registered, NULL);
+  nassertr(format->is_registered(), NULL);
+
+  MutexHolder holder(_formats_lock);
+
+  Formats::iterator fi;
+  fi = _premunge_formats.find(format);
+  if (fi != _premunge_formats.end()) {
+    // This format was previously munged, so the answer will be the
+    // same.
+    return (*fi).second;
+  }
+
+  // We have to munge this format for the first time.
+  CPT(GeomVertexFormat) derived_format = premunge_format_impl(format);
+  nassertr(derived_format->is_registered(), NULL);
+
+  // Store the answer in the map, so we can quickly get it next time.
+  bool inserted = _premunge_formats.insert(Formats::value_type(format, derived_format)).second;
+  nassertr(inserted, NULL);
+
+  return derived_format;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomMunger::premunge_format_impl
+//       Access: Protected, Virtual
+//  Description: Given a source GeomVertexFormat, converts it if
+//               necessary to the appropriate format for rendering.
+////////////////////////////////////////////////////////////////////
+CPT(GeomVertexFormat) GeomMunger::
+premunge_format_impl(const GeomVertexFormat *orig) {
+  return orig;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomMunger::premunge_data_impl
+//       Access: Protected, Virtual
+//  Description: Given a source GeomVertexData, converts it as
+//               necessary for rendering.
+////////////////////////////////////////////////////////////////////
+CPT(GeomVertexData) GeomMunger::
+premunge_data_impl(const GeomVertexData *data) {
+  nassertr(_is_registered, NULL);
+
+  CPT(GeomVertexFormat) orig_format = data->get_format();
+  CPT(GeomVertexFormat) new_format = premunge_format(orig_format);
+
+  if (new_format == orig_format) {
+    // Trivial case.
+    return data;
+  }
+
+  return data->convert_to(new_format);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomMunger::premunge_geom_impl
+//       Access: Protected, Virtual
+//  Description: Converts a Geom and/or its data as necessary.
+////////////////////////////////////////////////////////////////////
+bool GeomMunger::
+premunge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &) {
+  // The default implementation does nothing (the work has already
+  // been done in premunge_format_impl() and premunge_data_impl()).
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomMunger::compare_to_impl
 //     Function: GeomMunger::compare_to_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 15 - 3
panda/src/gobj/geomMunger.h

@@ -71,7 +71,7 @@ public:
   INLINE static void unregister_mungers_for_gsg(GraphicsStateGuardianBase *gsg);
   INLINE static void unregister_mungers_for_gsg(GraphicsStateGuardianBase *gsg);
 
 
   INLINE CPT(GeomVertexFormat) munge_format(const GeomVertexFormat *format,
   INLINE CPT(GeomVertexFormat) munge_format(const GeomVertexFormat *format,
-                                              const GeomVertexAnimationSpec &animation) const;
+                                            const GeomVertexAnimationSpec &animation) const;
 
 
   INLINE CPT(GeomVertexData) munge_data(const GeomVertexData *data) const;
   INLINE CPT(GeomVertexData) munge_data(const GeomVertexData *data) const;
   void remove_data(const GeomVertexData *data);
   void remove_data(const GeomVertexData *data);
@@ -79,6 +79,10 @@ public:
   void munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
   void munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
                   Thread *current_thread);
                   Thread *current_thread);
 
 
+  INLINE CPT(GeomVertexFormat) premunge_format(const GeomVertexFormat *format) const;
+  INLINE CPT(GeomVertexData) premunge_data(const GeomVertexData *data) const;
+  INLINE void premunge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data) const;
+
 public:
 public:
   INLINE int compare_to(const GeomMunger &other) const;
   INLINE int compare_to(const GeomMunger &other) const;
   INLINE int geom_compare_to(const GeomMunger &other) const;
   INLINE int geom_compare_to(const GeomMunger &other) const;
@@ -87,13 +91,20 @@ protected:
   INLINE void unregister_myself();
   INLINE void unregister_myself();
 
 
   CPT(GeomVertexFormat) do_munge_format(const GeomVertexFormat *format,
   CPT(GeomVertexFormat) do_munge_format(const GeomVertexFormat *format,
-                                          const GeomVertexAnimationSpec &animation);
+                                        const GeomVertexAnimationSpec &animation);
 
 
   virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig,
   virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig,
-                                                    const GeomVertexAnimationSpec &animation);
+                                                  const GeomVertexAnimationSpec &animation);
   virtual CPT(GeomVertexData) munge_data_impl(const GeomVertexData *data);
   virtual CPT(GeomVertexData) munge_data_impl(const GeomVertexData *data);
   virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
   virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
                                Thread *current_thread);
                                Thread *current_thread);
+
+
+  CPT(GeomVertexFormat) do_premunge_format(const GeomVertexFormat *format);
+  virtual CPT(GeomVertexFormat) premunge_format_impl(const GeomVertexFormat *orig);
+  virtual CPT(GeomVertexData) premunge_data_impl(const GeomVertexData *data);
+  virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data);
+
   virtual int compare_to_impl(const GeomMunger *other) const;
   virtual int compare_to_impl(const GeomMunger *other) const;
   virtual int geom_compare_to_impl(const GeomMunger *other) const;
   virtual int geom_compare_to_impl(const GeomMunger *other) const;
 
 
@@ -116,6 +127,7 @@ private:
   typedef pmap<CPT(GeomVertexFormat), CPT(GeomVertexFormat) > Formats;
   typedef pmap<CPT(GeomVertexFormat), CPT(GeomVertexFormat) > Formats;
   typedef pmap<GeomVertexAnimationSpec, Formats> FormatsByAnimation;
   typedef pmap<GeomVertexAnimationSpec, Formats> FormatsByAnimation;
   FormatsByAnimation _formats_by_animation;
   FormatsByAnimation _formats_by_animation;
+  Formats _premunge_formats;
 
 
   // This mutex protects the above.
   // This mutex protects the above.
   Mutex _formats_lock;
   Mutex _formats_lock;

+ 40 - 1
panda/src/gobj/geomVertexArrayFormat.cxx

@@ -221,7 +221,7 @@ add_column(InternalName *name, int num_components,
   }
   }
 
 
   return add_column(GeomVertexColumn(name, num_components, 
   return add_column(GeomVertexColumn(name, num_components, 
-                                       numeric_type, contents, start));
+                                     numeric_type, contents, start));
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -391,6 +391,35 @@ is_data_subset_of(const GeomVertexArrayFormat &other) const {
   return (i == _columns.size());
   return (i == _columns.size());
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayFormat::count_unused_space
+//       Access: Published
+//  Description: Returns the number of bytes per row that are not
+//               assigned to any column.
+////////////////////////////////////////////////////////////////////
+int GeomVertexArrayFormat::
+count_unused_space() const {
+  consider_sort_columns();
+
+  int unused_space = 0;
+  int last_pos = 0;
+
+  Columns::const_iterator ci;
+  for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+    const GeomVertexColumn *column = (*ci);
+    if (column->get_start() > last_pos) {
+      unused_space += (column->get_start() - last_pos);
+    }
+    last_pos = column->get_start() + column->get_total_bytes();
+  }
+
+  if (_stride > last_pos) {
+    unused_space += (_stride - last_pos);
+  }
+
+  return unused_space;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayFormat::output
 //     Function: GeomVertexArrayFormat::output
 //       Access: Published
 //       Access: Published
@@ -399,11 +428,21 @@ is_data_subset_of(const GeomVertexArrayFormat &other) const {
 void GeomVertexArrayFormat::
 void GeomVertexArrayFormat::
 output(ostream &out) const {
 output(ostream &out) const {
   Columns::const_iterator ci;
   Columns::const_iterator ci;
+  int last_pos = 0;
   out << "[";
   out << "[";
   for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
   for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
     const GeomVertexColumn *column = (*ci);
     const GeomVertexColumn *column = (*ci);
+    if (column->get_start() > last_pos) {
+      out << " ..." << (column->get_start() - last_pos) << "...";
+    }
     out << " " << *column;
     out << " " << *column;
+    last_pos = column->get_start() + column->get_total_bytes();
   }
   }
+
+  if (_stride > last_pos) {
+    out << " ..." << (_stride - last_pos) << "...";
+  }
+
   out << " ]";
   out << " ]";
 }
 }
 
 

+ 1 - 0
panda/src/gobj/geomVertexArrayFormat.h

@@ -104,6 +104,7 @@ PUBLISHED:
   INLINE bool has_column(const InternalName *name) const;
   INLINE bool has_column(const InternalName *name) const;
 
 
   bool is_data_subset_of(const GeomVertexArrayFormat &other) const;
   bool is_data_subset_of(const GeomVertexArrayFormat &other) const;
+  int count_unused_space() const;
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
   void write(ostream &out, int indent_level = 0) const;

+ 30 - 0
panda/src/pgraph/geomNode.cxx

@@ -32,6 +32,7 @@
 #include "indent.h"
 #include "indent.h"
 #include "pset.h"
 #include "pset.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
+#include "graphicsStateGuardianBase.h"
 
 
 TypeHandle GeomNode::_type_handle;
 TypeHandle GeomNode::_type_handle;
 
 
@@ -639,6 +640,35 @@ is_geom_node() const {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::do_premunge
+//       Access: Public
+//  Description: Uses the indicated GSG to premunge the Geoms in this
+//               node to optimize them for eventual rendering.  See
+//               SceneGraphReducer::premunge().
+////////////////////////////////////////////////////////////////////
+void GeomNode::
+do_premunge(GraphicsStateGuardianBase *gsg,
+            const RenderState *node_state,
+            GeomTransformer &transformer) {
+  Thread *current_thread = Thread::get_current_thread();
+
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
+    CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
+
+    GeomList::iterator gi;
+    PT(GeomList) geoms = cdata->modify_geoms();
+    for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
+      GeomEntry &entry = (*gi);
+      CPT(RenderState) geom_state = node_state->compose(entry._state);
+      CPT(Geom) geom = entry._geom.get_read_pointer();
+      PT(GeomMunger) munger = gsg->get_geom_munger(geom_state, current_thread);
+      entry._geom = transformer.premunge_geom(geom, munger);
+    }
+  }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::compute_internal_bounds
 //     Function: GeomNode::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 6 - 0
panda/src/pgraph/geomNode.h

@@ -29,6 +29,8 @@
 #include "pvector.h"
 #include "pvector.h"
 #include "copyOnWritePointer.h"
 #include "copyOnWritePointer.h"
 
 
+class GraphicsStateGuardianBase;
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : GeomNode
 //       Class : GeomNode
 // Description : A node that holds Geom objects, renderable pieces of
 // Description : A node that holds Geom objects, renderable pieces of
@@ -89,6 +91,10 @@ public:
 
 
   virtual bool is_geom_node() const;
   virtual bool is_geom_node() const;
 
 
+  void do_premunge(GraphicsStateGuardianBase *gsg,
+                   const RenderState *node_state,
+                   GeomTransformer &transformer);
+
 protected:
 protected:
   virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
   virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
 
 

+ 28 - 0
panda/src/pgraph/geomTransformer.cxx

@@ -29,6 +29,7 @@
 #include "pStatTimer.h"
 #include "pStatTimer.h"
 #include "vector_int.h"
 #include "vector_int.h"
 #include "userVertexTransform.h"
 #include "userVertexTransform.h"
+#include "geomMunger.h"
 
 
 static PStatCollector apply_vertex_collector("*:Flatten:apply:vertex");
 static PStatCollector apply_vertex_collector("*:Flatten:apply:vertex");
 static PStatCollector apply_texcoord_collector("*:Flatten:apply:texcoord");
 static PStatCollector apply_texcoord_collector("*:Flatten:apply:texcoord");
@@ -709,3 +710,30 @@ collect_vertex_data(GeomNode *node, int collect_bits) {
 
 
   return num_created;
   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;
+}

+ 3 - 0
panda/src/pgraph/geomTransformer.h

@@ -28,6 +28,7 @@
 class GeomNode;
 class GeomNode;
 class RenderState;
 class RenderState;
 class InternalName;
 class InternalName;
+class GeomMunger;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : GeomTransformer
 //       Class : GeomTransformer
@@ -73,6 +74,8 @@ public:
   int collect_vertex_data(Geom *geom, int collect_bits);
   int collect_vertex_data(Geom *geom, int collect_bits);
   int collect_vertex_data(GeomNode *node, int collect_bits);
   int collect_vertex_data(GeomNode *node, int collect_bits);
 
 
+  PT(Geom) premunge_geom(const Geom *geom, GeomMunger *munger);
+
 private:
 private:
   int _max_collect_vertices;
   int _max_collect_vertices;
 
 

+ 27 - 0
panda/src/pgraph/nodePath.cxx

@@ -5570,6 +5570,33 @@ verify_complete(Thread *current_thread) const {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::premunge_scene
+//       Access: Published
+//  Description: Walks through the scene graph beginning at the bottom
+//               node, and internally adjusts any GeomVertexFormats
+//               for optimal rendering on the indicated GSG.  If this
+//               step is not done prior to rendering, the formats will
+//               be optimized at render time instead, for a small
+//               cost.
+//
+//               It is not normally necessary to do this on a model
+//               loaded directly from disk, since the loader will do
+//               this by default.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+premunge_scene(GraphicsStateGuardianBase *gsg) {
+  nassertv_always(!is_empty());
+
+  CPT(RenderState) state = RenderState::make_empty();
+  if (has_parent()) {
+    state = get_parent().get_net_state();
+  }
+
+  SceneGraphReducer gr;
+  gr.premunge(node(), gsg, state);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::prepare_scene
 //     Function: NodePath::prepare_scene
 //       Access: Published
 //       Access: Published

+ 1 - 0
panda/src/pgraph/nodePath.h

@@ -793,6 +793,7 @@ PUBLISHED:
   // Miscellaneous
   // Miscellaneous
   bool verify_complete(Thread *current_thread = Thread::get_current_thread()) const;
   bool verify_complete(Thread *current_thread = Thread::get_current_thread()) const;
 
 
+  void premunge_scene(GraphicsStateGuardianBase *gsg);
   void prepare_scene(GraphicsStateGuardianBase *gsg);
   void prepare_scene(GraphicsStateGuardianBase *gsg);
 
 
   void show_bounds();
   void show_bounds();

+ 24 - 0
panda/src/pgraph/renderState.cxx

@@ -911,6 +911,30 @@ clear_cache() {
   return orig_size - new_size;
   return orig_size - new_size;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::clear_munger_cache
+//       Access: Published, Static
+//  Description: Completely empties the cache of state + gsg ->
+//               munger, for all states and all gsg's.  Normally there
+//               is no need to empty this cache.
+////////////////////////////////////////////////////////////////////
+void RenderState::
+clear_munger_cache() {
+  ReMutexHolder holder(*_states_lock);
+
+  // First, we need to count the number of times each RenderState
+  // object is recorded in the cache.
+  typedef pmap<const RenderState *, int> StateCount;
+  StateCount state_count;
+
+  States::iterator si;
+  for (si = _states->begin(); si != _states->end(); ++si) {
+    RenderState *state = (RenderState *)(*si);
+    state->_mungers.clear();
+    state->_last_mi = state->_mungers.end();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::list_cycles
 //     Function: RenderState::list_cycles
 //       Access: Published, Static
 //       Access: Published, Static

+ 1 - 0
panda/src/pgraph/renderState.h

@@ -128,6 +128,7 @@ PUBLISHED:
   static int get_num_states();
   static int get_num_states();
   static int get_num_unused_states();
   static int get_num_unused_states();
   static int clear_cache();
   static int clear_cache();
+  static void clear_munger_cache();
   static void list_cycles(ostream &out);
   static void list_cycles(ostream &out);
   static void list_states(ostream &out);
   static void list_states(ostream &out);
   static bool validate_states();
   static bool validate_states();

+ 17 - 0
panda/src/pgraph/sceneGraphReducer.I

@@ -159,3 +159,20 @@ unify(PandaNode *root) {
   PStatTimer timer(_unify_collector);
   PStatTimer timer(_unify_collector);
   r_unify(root);
   r_unify(root);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::premunge
+//       Access: Published
+//  Description: Walks the scene graph rooted at this node and below,
+//               and uses the indicated GSG to premunge every Geom
+//               found to optimize it for eventual rendering on the
+//               indicated GSG.
+//
+//               This operation will also apply to stashed children.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneGraphReducer::
+premunge(PandaNode *root, GraphicsStateGuardianBase *gsg,
+         const RenderState *initial_state) {
+  PStatTimer timer(_premunge_collector);
+  r_premunge(root, gsg, initial_state);
+}

+ 30 - 0
panda/src/pgraph/sceneGraphReducer.cxx

@@ -31,6 +31,7 @@ PStatCollector SceneGraphReducer::_apply_collector("*:Flatten:apply");
 PStatCollector SceneGraphReducer::_collect_collector("*:Flatten:collect");
 PStatCollector SceneGraphReducer::_collect_collector("*:Flatten:collect");
 PStatCollector SceneGraphReducer::_make_nonindexed_collector("*:Flatten:make nonindexed");
 PStatCollector SceneGraphReducer::_make_nonindexed_collector("*:Flatten:make nonindexed");
 PStatCollector SceneGraphReducer::_unify_collector("*:Flatten:unify");
 PStatCollector SceneGraphReducer::_unify_collector("*:Flatten:unify");
+PStatCollector SceneGraphReducer::_premunge_collector("*:Premunge");
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::flatten
 //     Function: SceneGraphReducer::flatten
@@ -738,3 +739,32 @@ r_unify(PandaNode *node) {
     r_unify(children.get_child(i));
     r_unify(children.get_child(i));
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::r_premunge
+//       Access: Private
+//  Description: The recursive implementation of premunge().
+////////////////////////////////////////////////////////////////////
+void SceneGraphReducer::
+r_premunge(PandaNode *node, GraphicsStateGuardianBase *gsg,
+           const RenderState *state) {
+  CPT(RenderState) next_state = state->compose(node->get_state());
+
+  if (node->is_geom_node()) {
+    GeomNode *geom_node = DCAST(GeomNode, node);
+    geom_node->do_premunge(gsg, next_state, _transformer);
+  }
+
+  int i;
+  PandaNode::Children children = node->get_children();
+  int num_children = children.get_num_children();
+  for (i = 0; i < num_children; ++i) {
+    r_premunge(children.get_child(i), gsg, next_state);
+  }
+
+  PandaNode::Stashed stashed = node->get_stashed();
+  int num_stashed = stashed.get_num_stashed();
+  for (i = 0; i < num_stashed; ++i) {
+    r_premunge(stashed.get_stashed(i), gsg, next_state);
+  }
+}

+ 7 - 0
panda/src/pgraph/sceneGraphReducer.h

@@ -134,6 +134,9 @@ PUBLISHED:
   INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0);
   INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0);
   INLINE void unify(PandaNode *root);
   INLINE void unify(PandaNode *root);
 
 
+  INLINE void premunge(PandaNode *root, GraphicsStateGuardianBase *gsg,
+                       const RenderState *initial_state);
+
 protected:
 protected:
   void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
   void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
                        int attrib_types, GeomTransformer &transformer);
                        int attrib_types, GeomTransformer &transformer);
@@ -164,6 +167,9 @@ protected:
   int r_make_nonindexed(PandaNode *node, int collect_bits);
   int r_make_nonindexed(PandaNode *node, int collect_bits);
   void r_unify(PandaNode *node);
   void r_unify(PandaNode *node);
 
 
+  void r_premunge(PandaNode *node, GraphicsStateGuardianBase *gsg,
+                  const RenderState *state);
+
 private:
 private:
   float _combine_radius;
   float _combine_radius;
   GeomTransformer _transformer;
   GeomTransformer _transformer;
@@ -173,6 +179,7 @@ private:
   static PStatCollector _collect_collector;
   static PStatCollector _collect_collector;
   static PStatCollector _make_nonindexed_collector;
   static PStatCollector _make_nonindexed_collector;
   static PStatCollector _unify_collector;
   static PStatCollector _unify_collector;
+  static PStatCollector _premunge_collector;
 };
 };
 
 
 #include "sceneGraphReducer.I"
 #include "sceneGraphReducer.I"