瀏覽代碼

apply_texture_colors()

David Rose 17 年之前
父節點
當前提交
11ae34e109

+ 23 - 2
panda/src/gobj/geom.I

@@ -325,8 +325,9 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
   CDReader cdata(_cycler, current_thread);
   
   do_calc_tight_bounds(min_point, max_point, found_any, 
-                       vertex_data, got_mat, mat, cdata,
-                       current_thread);
+                       vertex_data, got_mat, mat, 
+                       InternalName::get_vertex(),
+                       cdata, current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -351,6 +352,26 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
                     current_thread);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::calc_tight_bounds
+//       Access: Public
+//  Description: Similar to calc_tight_bounds(), for UV coordinates or
+//               other named columns.
+////////////////////////////////////////////////////////////////////
+INLINE void Geom::
+calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
+                  bool &found_any, 
+                  const GeomVertexData *vertex_data,
+                  bool got_mat, const LMatrix4f &mat,
+                  const InternalName *column_name,
+                  Thread *current_thread) const {
+  CDReader cdata(_cycler, current_thread);
+  
+  do_calc_tight_bounds(min_point, max_point, found_any, 
+                       vertex_data, got_mat, mat, 
+                       column_name, cdata, current_thread);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::mark_internal_bounds_stale
 //       Access: Private

+ 5 - 2
panda/src/gobj/geom.cxx

@@ -1219,7 +1219,9 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
   LPoint3f min, max;
   bool found_any = false;
   do_calc_tight_bounds(min, max, found_any, vertex_data,
-		       false, LMatrix4f::ident_mat(), cdata, current_thread);
+		       false, LMatrix4f::ident_mat(), 
+                       InternalName::get_vertex(),
+                       cdata, current_thread);
 
   if (found_any) {
     // Then we put the bounding volume around both of those points.
@@ -1268,6 +1270,7 @@ do_calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
 		     bool &found_any, 
 		     const GeomVertexData *vertex_data,
 		     bool got_mat, const LMatrix4f &mat,
+                     const InternalName *column_name,
                      const CData *cdata, Thread *current_thread) const {
   Primitives::const_iterator pi;
   for (pi = cdata->_primitives.begin(); 
@@ -1275,7 +1278,7 @@ do_calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
        ++pi) {
     CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
     prim->calc_tight_bounds(min_point, max_point, found_any, vertex_data,
-                            got_mat, mat, current_thread);
+                            got_mat, mat, column_name, current_thread);
   }
 }
 

+ 7 - 0
panda/src/gobj/geom.h

@@ -151,6 +151,12 @@ public:
                                 Thread *current_thread) const;
   INLINE void calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
                                 bool &found_any, Thread *current_thread) const;
+  INLINE void calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
+				bool &found_any, 
+				const GeomVertexData *vertex_data,
+				bool got_mat, const LMatrix4f &mat,
+                                const InternalName *column_name,
+                                Thread *current_thread) const;
 
   static UpdateSeq get_next_modified();
 
@@ -164,6 +170,7 @@ private:
 			    bool &found_any, 
 			    const GeomVertexData *vertex_data,
 			    bool got_mat, const LMatrix4f &mat,
+                            const InternalName *column_name,
                             const CData *cdata, Thread *current_thread) const;
 
   void clear_prepared(PreparedGraphicsObjects *prepared_objects);

+ 8 - 6
panda/src/gobj/geomPrimitive.cxx

@@ -1319,19 +1319,21 @@ get_highest_index_value(NumericType index_type) {
 //     Function: GeomPrimitive::calc_tight_bounds
 //       Access: Public, Virtual
 //  Description: Expands min_point and max_point to include all of the
-//               vertices in the Geom, if any.  found_any is set true
-//               if any points are found.  It is the caller's
-//               responsibility to initialize min_point, max_point,
-//               and found_any before calling this function.
+//               vertices in the Geom, if any (or the data of any
+//               point type, for instance, texture coordinates--based
+//               on the column name).  found_any is set true if any
+//               points are found.  It is the caller's responsibility
+//               to initialize min_point, max_point, and found_any
+//               before calling this function.
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
                   bool &found_any, 
                   const GeomVertexData *vertex_data,
                   bool got_mat, const LMatrix4f &mat,
+                  const InternalName *column_name,
                   Thread *current_thread) const {
-  GeomVertexReader reader(vertex_data, InternalName::get_vertex(),
-                          current_thread);
+  GeomVertexReader reader(vertex_data, column_name, current_thread);
   if (!reader.has_column()) {
     // No vertex data.
     return;

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

@@ -198,6 +198,7 @@ public:
                          bool &found_any, 
                          const GeomVertexData *vertex_data,
                          bool got_mat, const LMatrix4f &mat,
+                         const InternalName *column_name,
                          Thread *current_thread) const;
 
 protected:

+ 5 - 1
panda/src/pgraph/geomNode.cxx

@@ -292,7 +292,11 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 
-  transformer.register_vertices(this);
+  if ((attrib_types & SceneGraphReducer::TT_apply_texture_color) != 0) {
+    transformer.apply_texture_colors(this, attribs._other);
+  }
+
+  transformer.register_vertices(this, false);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 25 - 0
panda/src/pgraph/geomTransformer.I

@@ -82,6 +82,31 @@ operator < (const GeomTransformer::SourceColors &other) const {
   return (_color.compare_to(other._color) < 0);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::SourceTextureColors::Ordering Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomTransformer::SourceTextureColors::
+operator < (const GeomTransformer::SourceTextureColors &other) const {
+  if (_vertex_data != other._vertex_data) {
+    return _vertex_data < other._vertex_data;
+  }
+  if (_tex != other._tex) {
+    return _tex < other._tex;
+  }
+  if (_ts != other._ts) {
+    return _ts < other._ts;
+  }
+  if (_tma != other._tma) {
+    return _tma < other._tma;
+  }
+  if (_keep_vertex_color != other._keep_vertex_color) {
+    return (int)_keep_vertex_color < (int)other._keep_vertex_color;
+  }
+  return (_base_color.compare_to(other._base_color) < 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomTransformer::SourceFormat::Ordering Operator
 //       Access: Public

+ 275 - 3
panda/src/pgraph/geomTransformer.cxx

@@ -26,12 +26,15 @@
 #include "vector_int.h"
 #include "userVertexTransform.h"
 #include "geomMunger.h"
+#include "texture.h"
+#include "texturePeeker.h"
 #include "config_pgraph.h"
 
 PStatCollector GeomTransformer::_apply_vertex_collector("*:Flatten:apply:vertex");
 PStatCollector GeomTransformer::_apply_texcoord_collector("*:Flatten:apply:texcoord");
 PStatCollector GeomTransformer::_apply_set_color_collector("*:Flatten:apply:set color");
 PStatCollector GeomTransformer::_apply_scale_color_collector("*:Flatten:apply:scale color");
+PStatCollector GeomTransformer::_apply_texture_color_collector("*:Flatten:apply:texture color");
 PStatCollector GeomTransformer::_apply_set_format_collector("*:Flatten:apply:set format");
 
 TypeHandle GeomTransformer::NewCollectedData::_type_handle;
@@ -77,9 +80,12 @@ GeomTransformer::
 //               unused vertices.
 ////////////////////////////////////////////////////////////////////
 void GeomTransformer::
-register_vertices(Geom *geom) {
+register_vertices(Geom *geom, bool might_have_unused) {
   VertexDataAssoc &assoc = _vdata_assoc[geom->get_vertex_data()];
   assoc._geoms.push_back(geom);
+  if (might_have_unused) {
+    assoc._might_have_unused = true;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -90,7 +96,7 @@ register_vertices(Geom *geom) {
 //               unused vertices.
 ////////////////////////////////////////////////////////////////////
 void GeomTransformer::
-register_vertices(GeomNode *node) {
+register_vertices(GeomNode *node, bool might_have_unused) {
   Thread *current_thread = Thread::get_current_thread();
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler, current_thread) {
     GeomNode::CDStageWriter cdata(node->_cycler, pipeline_stage, current_thread);
@@ -99,7 +105,7 @@ register_vertices(GeomNode *node) {
     for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
       GeomNode::GeomEntry &entry = (*gi);
       PT(Geom) geom = entry._geom.get_write_pointer();
-      register_vertices(geom);
+      register_vertices(geom, might_have_unused);
     }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler);
@@ -425,6 +431,272 @@ transform_colors(GeomNode *node, const LVecBase4f &scale) {
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::apply_texture_colors
+//       Access: Public
+//  Description: Removes textures from Geoms by applying the texture
+//               colors to the vertices.  
+//
+//               See apply_texure_colors(GeomNode *, RenderState *).
+////////////////////////////////////////////////////////////////////
+bool GeomTransformer::
+apply_texture_colors(Geom *geom, TextureStage *ts, Texture *tex, 
+                     const TexMatrixAttrib *tma, const Colorf &base_color,
+                     bool keep_vertex_color) {
+  PStatTimer timer(_apply_texture_color_collector);
+
+  nassertr(geom != (Geom *)NULL, false);
+
+  PT(TexturePeeker) peeker = tex->peek();
+  if (peeker == (TexturePeeker *)NULL) {
+    return false;
+  }
+
+  if (peeker->get_x_size() == 1 && 
+      peeker->get_y_size() == 1 && 
+      peeker->get_z_size() == 1) {
+    // If it's just a one-pixel texture (e.g. a simple ram image),
+    // don't bother scanning the UV's.  Just extract the color and
+    // apply it.
+    Colorf color;
+    peeker->lookup(color, 0.0f, 0.0f);
+    color.set(color[0] * base_color[0],
+              color[1] * base_color[1],
+              color[2] * base_color[2],
+              color[3] * base_color[3]);
+    if (keep_vertex_color) {
+      return transform_colors(geom, color);
+    } else {
+      return set_color(geom, color);
+    }
+  }
+
+  bool got_mat = false;
+  LMatrix4f mat = LMatrix4f::ident_mat();
+  if (tma != (TexMatrixAttrib *)NULL && tma->has_stage(ts)) {
+    mat = tma->get_mat(ts);
+    got_mat = !mat.almost_equal(LMatrix4f::ident_mat());
+  }
+
+  // This version of the code just applied one overall flat color to
+  // the entire mesh.  Turned out not to be good enough.  Instead,
+  // we'll look up each vertex in the texture map and apply the
+  // nearest color to the vertex.
+  /*
+  // Scan the UV's to get the used range.  This is particularly
+  // necessary for palettized textures.
+
+  LPoint3f min_point, max_point;
+  bool found_any = false;
+  geom->calc_tight_bounds(min_point, max_point, found_any,
+                          geom->get_vertex_data(),
+                          got_mat, mat,
+                          ts->get_texcoord_name(),
+                          Thread::get_current_thread());
+  if (found_any) {
+    // Now use that UV range to determine the overall color of the
+    // geom's texture.
+    Colorf color;
+    peeker->filter_rect(color, 
+                        min_point[0], min_point[1], min_point[2],
+                        max_point[0], max_point[1], max_point[2]);
+    color.set(color[0] * base_color[0],
+              color[1] * base_color[1],
+              color[2] * base_color[2],
+              color[3] * base_color[3]);
+    if (keep_vertex_color) {
+      return transform_colors(geom, color);
+    } else {
+      return set_color(geom, color);
+    }
+  }
+
+  return false;
+  */
+
+  SourceTextureColors stc;
+  stc._ts = ts;
+  stc._tex = tex;
+  stc._tma = tma;
+  stc._base_color = base_color;
+  stc._keep_vertex_color = keep_vertex_color;
+  stc._vertex_data = geom->get_vertex_data();
+  
+  NewVertexData &new_data = _tex_colors[stc];
+  if (new_data._vdata.is_null()) {
+    // We have not yet applied these texture colors.  Do so now.
+
+    PT(GeomVertexData) vdata;
+
+    // Make sure the vdata has a color column.
+    if (stc._vertex_data->has_column(InternalName::get_color())) {
+      vdata = new GeomVertexData(*stc._vertex_data);
+    } else {
+      // Create a color column where there wasn't one before.
+      vdata = new GeomVertexData(*stc._vertex_data->set_color
+                                           (Colorf(1.0f, 1.0f, 1.0f, 1.0f), 1, Geom::NT_packed_dabc, Geom::C_color));
+      keep_vertex_color = false;
+    }
+    
+    // Check whether it has 2-d or 3-d texture coordinates.
+    bool tex3d = false;
+    const GeomVertexColumn *column = vdata->get_format()->get_column(ts->get_texcoord_name());
+    if (column == (GeomVertexColumn *)NULL) {
+      return false;
+    }
+    if (column->get_num_components() >= 3) {
+      tex3d = true;
+    }
+    
+    // Now walk through the vertices and apply each color from the
+    // texture as we go.
+    if (keep_vertex_color) {
+      // We want to modulate the existing vertex color.
+      GeomVertexReader gtexcoord(vdata, ts->get_texcoord_name());
+      GeomVertexRewriter gcolor(vdata, InternalName::get_color());
+      
+      if (got_mat || tex3d) {
+        while (!gtexcoord.is_at_end()) {
+          TexCoord3f p = gtexcoord.get_data3f();
+          Colorf c = gcolor.get_data4f();
+          p = p * mat;
+          Colorf color;
+          peeker->lookup(color, p[0], p[1], p[2]);
+          color.set(color[0] * base_color[0] * c[0],
+                    color[1] * base_color[1] * c[1],
+                    color[2] * base_color[2] * c[2],
+                    color[3] * base_color[3] * c[3]);
+          gcolor.set_data4f(color);
+        }
+      } else {
+        while (!gtexcoord.is_at_end()) {
+          TexCoordf p = gtexcoord.get_data2f();
+          Colorf c = gcolor.get_data4f();
+          Colorf color;
+          peeker->lookup(color, p[0], p[1]);
+          color.set(color[0] * base_color[0] * c[0],
+                    color[1] * base_color[1] * c[1],
+                    color[2] * base_color[2] * c[2],
+                    color[3] * base_color[3] * c[3]);
+          gcolor.set_data4f(color);
+        }
+      }
+    } else {
+      // We want to replace any existing vertex color.
+      GeomVertexReader gtexcoord(vdata, ts->get_texcoord_name());
+      GeomVertexWriter gcolor(vdata, InternalName::get_color());
+      
+      if (got_mat || tex3d) {
+        while (!gtexcoord.is_at_end()) {
+          TexCoord3f p = gtexcoord.get_data3f();
+          p = p * mat;
+          Colorf color;
+          peeker->lookup(color, p[0], p[1], p[2]);
+          color.set(color[0] * base_color[0],
+                    color[1] * base_color[1],
+                    color[2] * base_color[2],
+                    color[3] * base_color[3]);
+          gcolor.set_data4f(color);
+        }
+      } else {
+        while (!gtexcoord.is_at_end()) {
+          TexCoordf p = gtexcoord.get_data2f();
+          Colorf color;
+          peeker->lookup(color, p[0], p[1]);
+          color.set(color[0] * base_color[0],
+                    color[1] * base_color[1],
+                    color[2] * base_color[2],
+                    color[3] * base_color[3]);
+          gcolor.set_data4f(color);
+        }
+      }
+    }
+
+    new_data._vdata = vdata;
+  }
+
+  geom->set_vertex_data(new_data._vdata);
+  VertexDataAssoc &assoc = _vdata_assoc[new_data._vdata];
+  if (stc._vertex_data->get_ref_count() > 1) {
+    _vdata_assoc[new_data._vdata]._might_have_unused = true;
+    _vdata_assoc[stc._vertex_data]._might_have_unused = true;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTransformer::apply_texture_colors
+//       Access: Public
+//  Description: Removes textures from Geoms by applying the texture
+//               colors to the vertices.  This is primarily useful to
+//               simplify a low-LOD model.
+//
+//               Only the bottommost texture is used (if there is more
+//               than one), and it is applied as if it were
+//               M_modulate, and WM_repeat, regardless of its actual
+//               settings.  If the texture has a simple_ram_image,
+//               this may be used if the main image isn't resident.
+//
+//               After this call, there will be no texturing specified
+//               on the GeomNode level.  Of course, there might still
+//               be texturing inherited from above.
+////////////////////////////////////////////////////////////////////
+bool GeomTransformer::
+apply_texture_colors(GeomNode *node, const RenderState *state) {
+  bool any_changed = false;
+
+  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);
+    CPT(RenderState) geom_state = state->compose(entry._state);
+
+    const TextureAttrib *ta = geom_state->get_texture();
+    if (ta != (TextureAttrib *)NULL) {
+      CPT(TextureAttrib) ta2 = ta->filter_to_max(1);
+      if (ta2->get_num_on_stages() > 0) {
+        TextureStage *ts = ta2->get_on_stage(0);
+        Texture *tex = ta2->get_on_texture(ts);
+        const TexMatrixAttrib *tma = geom_state->get_tex_matrix();
+
+        const ColorAttrib *ca = geom_state->get_color();
+        Colorf base_color(1.0f, 1.0f, 1.0f, 1.0f);
+        bool keep_vertex_color = true;
+        if (ca != (ColorAttrib *)NULL && ca->get_color_type() == ColorAttrib::T_flat) {
+          base_color = ca->get_color();
+          keep_vertex_color = false;
+        }
+
+        PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
+        if (apply_texture_colors(new_geom, ts, tex, tma, base_color, keep_vertex_color)) {
+          entry._geom = new_geom;
+          any_changed = true;
+
+          if (new_geom->get_vertex_data()->has_column(InternalName::get_color())) {
+            // Ensure we have a ColorAttrib::make_vertex() attrib.
+            CPT(RenderState) color_state = entry._state->set_attrib(ColorAttrib::make_vertex());
+            if (entry._state != color_state) {
+              entry._state = color_state;
+              any_changed = true;
+            }
+          }
+        }
+
+        // Also remove any texture references from the GeomState.
+        CPT(RenderState) no_tex_state = entry._state->remove_attrib(TextureAttrib::get_class_type());
+        if (entry._state != no_tex_state) {
+          entry._state = no_tex_state;
+          any_changed = true;
+        }
+      }
+    }
+  }
+
+  return any_changed;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomTransformer::apply_state
 //       Access: Public

+ 31 - 2
panda/src/pgraph/geomTransformer.h

@@ -25,6 +25,7 @@ class GeomNode;
 class RenderState;
 class InternalName;
 class GeomMunger;
+class Texture;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : GeomTransformer
@@ -51,8 +52,8 @@ public:
   INLINE int get_max_collect_vertices() const;
   INLINE void set_max_collect_vertices(int max_collect_vertices);
 
-  void register_vertices(Geom *geom);
-  void register_vertices(GeomNode *node);
+  void register_vertices(Geom *geom, bool might_have_unused);
+  void register_vertices(GeomNode *node, bool might_have_unused);
 
   bool transform_vertices(Geom *geom, const LMatrix4f &mat);
   bool transform_vertices(GeomNode *node, const LMatrix4f &mat);
@@ -68,6 +69,11 @@ public:
   bool transform_colors(Geom *geom, const LVecBase4f &scale);
   bool transform_colors(GeomNode *node, const LVecBase4f &scale);
 
+  bool apply_texture_colors(Geom *geom, TextureStage *ts, Texture *tex, 
+                            const TexMatrixAttrib *tma,
+                            const Colorf &base_color, bool keep_vertex_color);
+  bool apply_texture_colors(GeomNode *node, const RenderState *state);
+
   bool apply_state(GeomNode *node, const RenderState *state);
 
   bool set_format(Geom *geom, const GeomVertexFormat *new_format);
@@ -88,6 +94,12 @@ public:
 
   PT(Geom) premunge_geom(const Geom *geom, GeomMunger *munger);
 
+private:
+  static void get_texel_color(Colorf &color, float u, float v, 
+                              const unsigned char *image, 
+                              int x_size, int y_size);
+
+
 private:
   int _max_collect_vertices;
 
@@ -156,6 +168,22 @@ private:
   // modified from the original colors (e.g. via a ColorScaleAttrib).
   NewColors _fcolors, _tcolors;
 
+  // The table of GeomVertexData objects whose texture colors have
+  // been applied.
+  class SourceTextureColors {
+  public:
+    INLINE bool operator < (const SourceTextureColors &other) const;
+
+    TextureStage *_ts;
+    Texture *_tex;
+    const TexMatrixAttrib *_tma;
+    Colorf _base_color;
+    bool _keep_vertex_color;
+    CPT(GeomVertexData) _vertex_data;
+  };
+  typedef pmap<SourceTextureColors, NewVertexData> NewTextureColors;
+  NewTextureColors _tex_colors;
+
   // The table of GeomVertexData objects whose vertex formats have
   // been modified. For set_format(): record (format + vertex_data) ->
   // vertex_data.
@@ -252,6 +280,7 @@ private:
   static PStatCollector _apply_texcoord_collector;
   static PStatCollector _apply_set_color_collector;
   static PStatCollector _apply_scale_color_collector;
+  static PStatCollector _apply_texture_color_collector;
   static PStatCollector _apply_set_format_collector;
     
 public:

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

@@ -6006,6 +6006,33 @@ flatten_strong() {
   return num_removed;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::apply_texture_colors
+//       Access: Published
+//  Description: Removes textures from Geoms at this node and below by
+//               applying the texture colors to the vertices.  This is
+//               primarily useful to simplify a low-LOD model.  The
+//               texture colors are replaced by flat colors that
+//               approximate the original textures.
+//
+//               Only the bottommost texture on each Geom is used (if
+//               there is more than one), and it is applied as if it
+//               were M_modulate, and WM_repeat, regardless of its
+//               actual settings.  If the texture has a
+//               simple_ram_image, this may be used if the main image
+//               isn't resident.
+//
+//               After this call, there will be no texturing specified
+//               at this level and below.  Of course, there might
+//               still be texturing inherited from above.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+apply_texture_colors() {
+  nassertv_always(!is_empty());
+  SceneGraphReducer gr;
+  gr.apply_attribs(node(), SceneGraphReducer::TT_apply_texture_color | SceneGraphReducer::TT_tex_matrix | SceneGraphReducer::TT_other);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::find_net_tag
 //       Access: Published

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

@@ -819,6 +819,7 @@ PUBLISHED:
   int flatten_light();
   int flatten_medium();
   int flatten_strong();
+  void apply_texture_colors();
   INLINE int clear_model_nodes();
 
   INLINE void set_tag(const string &key, const string &value);

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

@@ -31,6 +31,7 @@ PStatCollector SceneGraphReducer::_compatible_state_collector("*:Flatten:compati
 PStatCollector SceneGraphReducer::_collect_collector("*:Flatten:collect");
 PStatCollector SceneGraphReducer::_make_nonindexed_collector("*:Flatten:make nonindexed");
 PStatCollector SceneGraphReducer::_unify_collector("*:Flatten:unify");
+PStatCollector SceneGraphReducer::_remove_unused_collector("*:Flatten:remove unused vertices");
 PStatCollector SceneGraphReducer::_premunge_collector("*:Premunge");
 
 ////////////////////////////////////////////////////////////////////
@@ -183,6 +184,25 @@ unify(PandaNode *root, bool preserve_order) {
   r_unify(root, max_indices, preserve_order);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::remove_unused_vertices
+//       Access: Published
+//  Description: Removes any vertices in GeomVertexDatas that are no
+//               longer used at this level and below.  This requires
+//               remapping vertex indices in all of the
+//               GeomPrimitives, to remove holes in the
+//               GeomVertexDatas.  It is normally not necessary to
+//               call this explicitly.
+////////////////////////////////////////////////////////////////////
+void SceneGraphReducer::
+remove_unused_vertices(PandaNode *root) {
+  PStatTimer timer(_remove_unused_collector);
+
+  r_register_vertices(root, _transformer);
+  _transformer.finish_apply();
+  Thread::consider_yield();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::decompose
 //       Access: Published
@@ -909,6 +929,27 @@ r_unify(PandaNode *node, int max_indices, bool preserve_order) {
   Thread::consider_yield();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::r_register_vertices
+//       Access: Private
+//  Description: Recursively calls
+//               GeomTransformer::register_vertices() on all GeomNodes
+//               at the indicated root and below.
+////////////////////////////////////////////////////////////////////
+void SceneGraphReducer::
+r_register_vertices(PandaNode *node, GeomTransformer &transformer) {
+  if (node->is_geom_node()) {
+    GeomNode *geom_node = DCAST(GeomNode, node);
+    transformer.register_vertices(geom_node, true);
+  }
+
+  PandaNode::Children children = node->get_children();
+  int num_children = children.get_num_children();
+  for (int i = 0; i < num_children; ++i) {
+    r_register_vertices(children.get_child(i), transformer);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::r_decompose
 //       Access: Private

+ 12 - 8
panda/src/pgraph/sceneGraphReducer.h

@@ -46,13 +46,14 @@ PUBLISHED:
   INLINE ~SceneGraphReducer();
 
   enum AttribTypes {
-    TT_transform       = 0x001,
-    TT_color           = 0x002,
-    TT_color_scale     = 0x004,
-    TT_tex_matrix      = 0x008,
-    TT_clip_plane      = 0x010,
-    TT_cull_face       = 0x020,
-    TT_other           = 0x040,
+    TT_transform           = 0x001,
+    TT_color               = 0x002,
+    TT_color_scale         = 0x004,
+    TT_tex_matrix          = 0x008,
+    TT_clip_plane          = 0x010,
+    TT_cull_face           = 0x020,
+    TT_apply_texture_color = 0x040,
+    TT_other               = 0x080,
   };
 
   enum CombineSiblings {
@@ -126,7 +127,7 @@ PUBLISHED:
   INLINE void set_combine_radius(float combine_radius);
   INLINE float get_combine_radius() const;
 
-  INLINE void apply_attribs(PandaNode *node, int attrib_types = ~(TT_clip_plane | TT_cull_face));
+  INLINE void apply_attribs(PandaNode *node, int attrib_types = ~(TT_clip_plane | TT_cull_face | TT_apply_texture_color));
   INLINE void apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
                             int attrib_types, GeomTransformer &transformer);
 
@@ -142,6 +143,7 @@ PUBLISHED:
   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);
+  void remove_unused_vertices(PandaNode *root);
 
   INLINE void premunge(PandaNode *root, const RenderState *initial_state);
 
@@ -179,6 +181,7 @@ protected:
                             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);
+  void r_register_vertices(PandaNode *node, GeomTransformer &transformer);
   void r_decompose(PandaNode *node);
 
   void r_premunge(PandaNode *node, const RenderState *state);
@@ -195,6 +198,7 @@ private:
   static PStatCollector _collect_collector;
   static PStatCollector _make_nonindexed_collector;
   static PStatCollector _unify_collector;
+  static PStatCollector _remove_unused_collector;
   static PStatCollector _premunge_collector;
 };