Browse Source

flesh out MultitexReducer some more

David Rose 21 years ago
parent
commit
7d1a5fc226

+ 11 - 1
panda/src/gobj/geom.I

@@ -368,11 +368,21 @@ get_next_normal(NormalIterator &niterator) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Geom::TexCoordIterator Geom::
 INLINE Geom::TexCoordIterator Geom::
 make_texcoord_iterator() const {
 make_texcoord_iterator() const {
+  return make_texcoord_iterator(TexCoordName::get_default());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::make_texcoord_iterator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Geom::TexCoordIterator Geom::
+make_texcoord_iterator(const TexCoordName *texcoord_name) const {
   check_config();
   check_config();
   TexCoordIterator i;
   TexCoordIterator i;
 
 
   TexCoordsByName::const_iterator tci = 
   TexCoordsByName::const_iterator tci = 
-    _texcoords_by_name.find(TexCoordName::get_default());
+    _texcoords_by_name.find(texcoord_name);
   if (tci != _texcoords_by_name.end()) {
   if (tci != _texcoords_by_name.end()) {
     i._array = (*tci).second._texcoords;
     i._array = (*tci).second._texcoords;
     i._index = (*tci).second._tindex;;
     i._index = (*tci).second._tindex;;

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

@@ -223,6 +223,7 @@ public:
   INLINE const Normalf &get_next_normal(NormalIterator &niterator) const;
   INLINE const Normalf &get_next_normal(NormalIterator &niterator) const;
 
 
   INLINE TexCoordIterator make_texcoord_iterator() const;
   INLINE TexCoordIterator make_texcoord_iterator() const;
+  INLINE TexCoordIterator make_texcoord_iterator(const TexCoordName *texcoord_name) const;
   INLINE const TexCoordf &get_next_texcoord(TexCoordIterator &tciterator) const;
   INLINE const TexCoordf &get_next_texcoord(TexCoordIterator &tciterator) const;
   void setup_multitexcoord_iterator(MultiTexCoordIterator &iterator,
   void setup_multitexcoord_iterator(MultiTexCoordIterator &iterator,
                                     const ActiveTextureStages &active_stages,
                                     const ActiveTextureStages &active_stages,

+ 31 - 1
panda/src/grutil/multitexReducer.I

@@ -17,6 +17,33 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MultitexReducer::scan
+//       Access: Published
+//  Description: Starts scanning the hierarchy beginning at the
+//               indicated node.  Any GeomNodes discovered in the
+//               hierarchy with multitexture will be added to internal
+//               structures in the MultitexReducer so that a future
+//               call to flatten() will operate on all of these at
+//               once.
+//
+//               The second parameter represents the NodePath from
+//               which to accumulate the state that is considered for
+//               the multitexture.  The default is to accumulate all
+//               the state from the root of the graph, but you may
+//               specify some other node here in order to not consider
+//               nodes above that as contributing to the state to be
+//               flattened.  This is particularly useful if you have
+//               some texture stage which is applied globally to a
+//               scene (for instance, a caustics effect), which you
+//               don't want to be considered for flattening by the
+//               MultitexReducer.
+////////////////////////////////////////////////////////////////////
+INLINE void MultitexReducer::
+scan(const NodePath &node, const NodePath &state_from) {
+  scan(node.node(), node.get_state(state_from), node.get_transform(state_from));
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MultitexReducer::StageInfo::operator <
 //     Function: MultitexReducer::StageInfo::operator <
 //       Access: Public
 //       Access: Public
@@ -43,7 +70,10 @@ operator < (const MultitexReducer::StageInfo &other) const {
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE MultitexReducer::GeomInfo::
 INLINE MultitexReducer::GeomInfo::
-GeomInfo(GeomNode *geom_node, int index) :
+GeomInfo(const RenderState *state, const RenderState *geom_net_state,
+         GeomNode *geom_node, int index) :
+  _state(state),
+  _geom_net_state(geom_net_state),
   _geom_node(geom_node),
   _geom_node(geom_node),
   _index(index)
   _index(index)
 {
 {

+ 349 - 60
panda/src/grutil/multitexReducer.cxx

@@ -26,12 +26,14 @@
 #include "graphicsChannel.h"
 #include "graphicsChannel.h"
 #include "graphicsLayer.h"
 #include "graphicsLayer.h"
 #include "displayRegion.h"
 #include "displayRegion.h"
-#include "nodePath.h"
 #include "camera.h"
 #include "camera.h"
 #include "orthographicLens.h"
 #include "orthographicLens.h"
 #include "cardMaker.h"
 #include "cardMaker.h"
+#include "colorAttrib.h"
+#include "colorScaleAttrib.h"
 #include "colorBlendAttrib.h"
 #include "colorBlendAttrib.h"
 #include "config_grutil.h"
 #include "config_grutil.h"
+#include "config_gobj.h"
 #include "dcast.h"
 #include "dcast.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -42,6 +44,8 @@
 MultitexReducer::
 MultitexReducer::
 MultitexReducer() {
 MultitexReducer() {
   _target_stage = TextureStage::get_default();
   _target_stage = TextureStage::get_default();
+  _use_geom = false;
+  _allow_tex_mat = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -81,8 +85,8 @@ clear() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MultitexReducer::
 void MultitexReducer::
 scan(PandaNode *node, const RenderState *state, const TransformState *transform) {
 scan(PandaNode *node, const RenderState *state, const TransformState *transform) {
-  CPT(RenderState) next_state = node->get_state()->compose(state);
-  CPT(TransformState) next_transform = node->get_transform()->compose(transform);
+  CPT(RenderState) next_state = state->compose(node->get_state());
+  CPT(TransformState) next_transform = transform->compose(node->get_transform());
 
 
   if (node->is_geom_node()) {
   if (node->is_geom_node()) {
     scan_geom_node(DCAST(GeomNode, node), next_state, next_transform);
     scan_geom_node(DCAST(GeomNode, node), next_state, next_transform);
@@ -107,6 +111,61 @@ set_target(TextureStage *stage) {
   _target_stage = stage;
   _target_stage = stage;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MultitexReducer::set_use_geom
+//       Access: Published
+//  Description: Indicates whether the actual geometry will be used to
+//               generate the textures.  
+//
+//               If this is set to true, the geometry discovered by
+//               scan() will be used to generate the textures, which
+//               allows for the vertex and polygon colors to be made
+//               part of the texture itself (and makes the M_decal
+//               multitexture mode more reliable).  However, this only
+//               works if the geometry does not contain multiple
+//               different polygons that map to the same UV range.
+//
+//               If this is set to false (the default), a plain flat
+//               card will be used to generate the textures, which is
+//               more robust in general, but the resulting texture
+//               will not include vertex colors and M_decal won't work
+//               properly.
+//
+//               Note that in case multiple sets of texture
+//               coordinates are in effect, then the additional sets
+//               will always use the geometry anyway regardless of the
+//               setting of this flag (but this will not affect vertex
+//               color).
+////////////////////////////////////////////////////////////////////
+void MultitexReducer::
+set_use_geom(bool use_geom) {
+  _use_geom = use_geom;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MultitexReducer::set_allow_tex_mat
+//       Access: Published
+//  Description: Indicates whether the resulting texture should be
+//               expected to be animated beyond its current range via
+//               a texture matrix (true), or whether the current range
+//               of texture coordinates will be sufficient forever
+//               (false).
+//
+//               If this is set to true, then the entire texture image
+//               must be generated, in the assumption that the user
+//               may animate the texture around on the surface after
+//               it has been composed.
+//
+//               If this is set to false (the default), then only the
+//               portion of the texture image which is actually in use
+//               must be generated, which may be a significant savings
+//               in texture memory.
+////////////////////////////////////////////////////////////////////
+void MultitexReducer::
+set_allow_tex_mat(bool allow_tex_mat) {
+  _allow_tex_mat = allow_tex_mat;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MultitexReducer::flatten
 //     Function: MultitexReducer::flatten
 //       Access: Published
 //       Access: Published
@@ -155,10 +214,35 @@ flatten(GraphicsOutput *window) {
     const GeomList &geom_list = (*mi).second;
     const GeomList &geom_list = (*mi).second;
 
 
     // Create an offscreen buffer in which to render the new texture.
     // Create an offscreen buffer in which to render the new texture.
-    int x_size, y_size, aniso_degree;
-    Texture::FilterType minfilter, magfilter;
-    determine_size(x_size, y_size, aniso_degree,
-                   minfilter, magfilter, stage_list);
+
+    // Start by choosing a model TextureStage to determine the new
+    // texture's properties.
+    const StageInfo &model_stage = stage_list[choose_model_stage(stage_list)];
+
+    Texture *model_tex = model_stage._tex;
+    int aniso_degree = model_tex->get_anisotropic_degree();
+    Texture::FilterType minfilter = model_tex->get_minfilter();
+    Texture::FilterType magfilter = model_tex->get_magfilter();
+
+    // What is the UV range of the model stage?
+    TexCoordf min_uv, max_uv;
+    determine_uv_range(min_uv, max_uv, model_stage, geom_list);
+
+    // Maybe we only use a small portion of the texture, or maybe we
+    // need to repeat the texture several times.
+    LVecBase2f uv_scale;
+    LVecBase2f uv_trans;
+    get_uv_scale(uv_scale, uv_trans, min_uv, max_uv);
+
+    // Also, if there is now a scale on the UV's (in conjunction with
+    // whatever texture matrix might be applied on the model stage),
+    // we may be able to adjust the image size accordingly, to keep
+    // the pixels at about the same scale--but we have to keep it to a
+    // power of 2.
+    int x_size;
+    int y_size;
+    choose_texture_size(x_size, y_size, model_stage, uv_scale,
+                        window);
 
 
     GraphicsOutput *buffer = 
     GraphicsOutput *buffer = 
       window->make_texture_buffer("multitex", x_size, y_size);
       window->make_texture_buffer("multitex", x_size, y_size);
@@ -178,6 +262,10 @@ flatten(GraphicsOutput *window) {
     lens->set_film_size(1.0f, 1.0f);
     lens->set_film_size(1.0f, 1.0f);
     lens->set_film_offset(0.5f, 0.5f);
     lens->set_film_offset(0.5f, 0.5f);
     lens->set_near_far(-1000.0f, 1000.0f);
     lens->set_near_far(-1000.0f, 1000.0f);
+    lens->set_view_mat(LMatrix4f(uv_scale[0], 0.0f, 0.0, 0.0f,
+                                 0.0f, 1.0f, 0.0, 0.0f,
+                                 0.0f, 0.0f, uv_scale[1], 0.0f,
+                                 uv_trans[0], 0.0f, uv_trans[1], 1.0f));
     cam_node->set_lens(lens);
     cam_node->set_lens(lens);
 
 
     // Create a root node for the buffer's scene graph, and set up
     // Create a root node for the buffer's scene graph, and set up
@@ -191,11 +279,22 @@ flatten(GraphicsOutput *window) {
     NodePath cam = render.attach_new_node(cam_node);
     NodePath cam = render.attach_new_node(cam_node);
     dr->set_camera(cam);
     dr->set_camera(cam);
 
 
-    // Put one plain white card in the background for the first
-    // texture layer to apply onto.
-    CardMaker cm("background");
-    cm.set_frame(0.0f, 1.0f, 0.0f, 1.0f);
-    render.attach_new_node(cm.generate());
+    if (!_use_geom) {
+      // Put one plain white card in the background for the first
+      // texture layer to apply onto.
+      
+      CardMaker cm("background");
+      cm.set_frame(0.0f, 1.0f, 0.0f, 1.0f);
+      render.attach_new_node(cm.generate());
+
+    } else {
+      // Put a colored model of the geometry in the background for the
+      // first texture layer to apply only.
+      PT(GeomNode) geom_node = new GeomNode("background");
+      transfer_geom(geom_node, NULL, geom_list, true);
+      
+      render.attach_new_node(geom_node);
+    }
 
 
     StageList::const_iterator si;
     StageList::const_iterator si;
     for (si = stage_list.begin(); si != stage_list.end(); ++si) {
     for (si = stage_list.begin(); si != stage_list.end(); ++si) {
@@ -216,6 +315,42 @@ flatten(GraphicsOutput *window) {
       CPT(RenderState) geom_state = 
       CPT(RenderState) geom_state = 
         geom_info._geom_node->get_geom_state(geom_info._index);
         geom_info._geom_node->get_geom_state(geom_info._index);
       geom_state = geom_state->add_attrib(new_ta);
       geom_state = geom_state->add_attrib(new_ta);
+
+      if (_use_geom) {
+        // If we have use_geom in effect, we have to be sure to
+        // disable coloring on the new fragment (the color has been
+        // baked into the texture).
+        geom_state = geom_state->add_attrib(ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 1.0f, 1.0f)));
+        geom_state = geom_state->add_attrib(ColorScaleAttrib::make_off());
+      }
+
+      // Determine what tex matrix should be on the Geom.
+      CPT(TransformState) tex_mat = TransformState::make_identity();
+
+      const RenderAttrib *ra = geom_info._state->get_attrib(TexMatrixAttrib::get_class_type());
+      if (ra != (const RenderAttrib *)NULL) {
+        // There is a texture matrix inherited from above; put an
+        // inverse matrix on the Geom to compensate.
+        const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, ra);
+        CPT(TransformState) tex_mat = tma->get_transform(_target_stage);
+      }
+
+      tex_mat = tex_mat->compose(TransformState::make_pos_hpr_scale
+                                 (LVecBase3f(uv_trans[0], uv_trans[1], 0.0f),
+                                  LVecBase3f(0.0f, 0.0f, 0.0f),
+                                  LVecBase3f(uv_scale[0], uv_scale[1], 1.0f)));
+
+      if (tex_mat->is_identity()) {
+        // There should be no texture matrix on the Geom.
+        geom_state = geom_state->remove_attrib(TexMatrixAttrib::get_class_type());
+      } else {
+        // The texture matrix should be as computed.
+        CPT(RenderAttrib) new_tma = TexMatrixAttrib::make
+          (_target_stage, tex_mat->invert_compose(TransformState::make_identity()));
+        geom_state = geom_state->add_attrib(new_tma);
+      }
+
+
       geom_info._geom_node->set_geom_state(geom_info._index, geom_state);
       geom_info._geom_node->set_geom_state(geom_info._index, geom_state);
     }
     }
   }
   }
@@ -239,7 +374,7 @@ scan_geom_node(GeomNode *node, const RenderState *state,
   int num_geoms = node->get_num_geoms();
   int num_geoms = node->get_num_geoms();
   for (int gi = 0; gi < num_geoms; gi++) {
   for (int gi = 0; gi < num_geoms; gi++) {
     CPT(RenderState) geom_net_state = 
     CPT(RenderState) geom_net_state = 
-      node->get_geom_state(gi)->compose(state);
+      state->compose(node->get_geom_state(gi));
 
 
     // Get out the net TextureAttrib and TexMatrixAttrib from the state.
     // Get out the net TextureAttrib and TexMatrixAttrib from the state.
     const RenderAttrib *attrib;
     const RenderAttrib *attrib;
@@ -267,7 +402,7 @@ scan_geom_node(GeomNode *node, const RenderState *state,
         stage_list.push_back(StageInfo(stage, ta, tma));
         stage_list.push_back(StageInfo(stage, ta, tma));
       }
       }
 
 
-      record_stage_list(stage_list, GeomInfo(node, gi));
+      record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
     }
     }
   }
   }
 }
 }
@@ -298,45 +433,182 @@ record_stage_list(const MultitexReducer::StageList &stage_list,
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MultitexReducer::determine_size
+//     Function: MultitexReducer::choose_model_stage
 //       Access: Private
 //       Access: Private
-//  Description: Tries to guess what size to make the new, collapsed
-//               texture based on the sizes of all of the textures
-//               used in the stage_list.
+//  Description: Chooses one of the TextureStages in the stage_list to
+//               serve as the model to determine the size and
+//               properties of the resulting texture.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void MultitexReducer::
-determine_size(int &x_size, int &y_size, int &aniso_degree,
-               Texture::FilterType &minfilter, Texture::FilterType &magfilter, 
-               const MultitexReducer::StageList &stage_list) const {
-  x_size = 0;
-  y_size = 0;
-  aniso_degree = 0;
-  minfilter = Texture::FT_nearest;
-  magfilter = Texture::FT_nearest;
-
-  StageList::const_iterator si;
-  for (si = stage_list.begin(); si != stage_list.end(); ++si) {
-    const StageInfo &stage_info = (*si);
-    Texture *tex = stage_info._tex;
-    PixelBuffer *pbuffer = tex->_pbuffer;
-
+size_t MultitexReducer::
+choose_model_stage(const MultitexReducer::StageList &stage_list) const {
+  for (size_t si = 0; si < stage_list.size(); si++) {
+    const StageInfo &stage_info = stage_list[si];
     if (stage_info._stage == _target_stage) {
     if (stage_info._stage == _target_stage) {
       // If we find the target stage, use that.
       // If we find the target stage, use that.
-      x_size = pbuffer->get_xsize();
-      y_size = pbuffer->get_ysize();
-      aniso_degree = tex->get_anisotropic_degree();
-      minfilter = tex->get_minfilter();
-      magfilter = tex->get_magfilter();
-      return;
+      return si;
+    }
+  }
+
+  // If none of the stages are the target stage, use the bottom image.
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MultitexReducer::determine_uv_range
+//       Access: Private
+//  Description: Determines what the effective UV range for the
+//               indicated texture is across its geoms.  Returns true
+//               if any UV's are found, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool MultitexReducer::
+determine_uv_range(TexCoordf &min_uv, TexCoordf &max_uv,
+                   const MultitexReducer::StageInfo &model_stage,
+                   const MultitexReducer::GeomList &geom_list) const {
+  const TexCoordName *model_name = model_stage._stage->get_texcoord_name();
+  bool got_any = false;
+
+  GeomList::const_iterator gi;
+  for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
+    const GeomInfo &geom_info = (*gi);
+    
+    PT(Geom) geom = 
+      geom_info._geom_node->get_geom(geom_info._index)->make_copy();
+
+    int num_vertices = geom->get_num_vertices();
+    if (geom->has_texcoords(model_name) && num_vertices > 0) {
+      Geom::TexCoordIterator ti = geom->make_texcoord_iterator(model_name);
+
+      int i = 0;
+      const TexCoordf &uv = geom->get_next_texcoord(ti);
+      if (!got_any) {
+        min_uv = max_uv = uv;
+        got_any = true;
+
+      } else {
+        min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
+        max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
+      }
+      ++i;
+
+      while (i < num_vertices) {
+        const TexCoordf &uv = geom->get_next_texcoord(ti);
+        min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
+        max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
+        ++i;
+      }
+    }
+  }
+
+  if (!got_any) {
+    min_uv.set(0.0f, 0.0f);
+    max_uv.set(1.0f, 1.0f);
+  }
+
+  return got_any;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MultitexReducer::get_uv_scale
+//       Access: Private
+//  Description: Chooses an appropriate transform to apply to all of
+//               the UV's on the generated texture, based on the
+//               coverage of the model stage.  If only a portion of
+//               the model stage is used, we scale the UV's up to zoom
+//               into that one portion; on the other hand, if the
+//               texture repeats many times, we scale the UV's down to
+//               to include all of the repeating image.
+////////////////////////////////////////////////////////////////////
+void MultitexReducer::
+get_uv_scale(LVecBase2f &uv_scale, LVecBase2f &uv_trans,
+             const TexCoordf &min_uv, const TexCoordf &max_uv) const {
+  if (max_uv[0] != min_uv[0]) {
+    uv_scale[0] = (max_uv[0] - min_uv[0]);
+  } else {
+    uv_scale[0] = 1.0f;
+  }
+
+  if (max_uv[1] != min_uv[1]) {
+    uv_scale[1] = (max_uv[1] - min_uv[1]);
+  } else {
+    uv_scale[1] = 1.0f;
+  }
+
+  uv_trans[0] = (min_uv[0] + max_uv[0]) / 2.0f - uv_scale[0] * 0.5f;
+  uv_trans[1] = (min_uv[1] + max_uv[1]) / 2.0f - uv_scale[1] * 0.5f;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MultitexReducer::choose_texture_size
+//       Access: Private
+//  Description: Chooses an appropriate size to make the new texture,
+//               based on the size of the original model stage's
+//               texture, and the scale applied to the UV's.
+////////////////////////////////////////////////////////////////////
+void MultitexReducer::
+choose_texture_size(int &x_size, int &y_size, 
+                    const MultitexReducer::StageInfo &model_stage, 
+                    const LVecBase2f &uv_scale,
+                    GraphicsOutput *window) const {
+  Texture *model_tex = model_stage._tex;
+  PixelBuffer *model_pbuffer = model_tex->_pbuffer;
+  
+  // Start with the same size as the model texture.
+  x_size = model_pbuffer->get_xsize();
+  y_size = model_pbuffer->get_ysize();
+
+  // But we might be looking at just a subset of that texture (scale <
+  // 1) or a superset of the texture (scale > 1).  In this case, we
+  // should adjust the pixel size accordingly, although we have to
+  // keep it to a power of 2.
+
+  LVecBase3f inherited_scale = model_stage._tex_mat->get_scale();
+  
+  float u_scale = inherited_scale[0] * uv_scale[0];
+  if (u_scale != 0.0f) {
+    while (u_scale >= 2.0f) {
+      x_size *= 2;
+      u_scale *= 0.5f;
+    }
+    while (u_scale <= 0.5f) {
+      x_size /= 2;
+      u_scale *= 2.0f;
     }
     }
+  }
 
 
-    // If we never run across the target stage, just use the maximum
-    // of all encountered textures.
-    x_size = max(x_size, pbuffer->get_xsize());
-    y_size = max(y_size, pbuffer->get_ysize());
-    aniso_degree = max(aniso_degree, tex->get_anisotropic_degree());
-    minfilter = (Texture::FilterType)max((int)minfilter, (int)tex->get_minfilter());
-    magfilter = (Texture::FilterType)max((int)magfilter, (int)tex->get_magfilter());
+  float v_scale = inherited_scale[1] * uv_scale[1];
+  if (v_scale != 0.0f) {
+    while (v_scale >= 2.0f) {
+      y_size *= 2;
+      v_scale *= 0.5f;
+    }
+    while (v_scale <= 0.5f) {
+      y_size /= 2;
+      v_scale *= 2.0f;
+    }
+  }
+
+  // Constrain the x_size and y_size to the max_texture_dimension.
+  if (max_texture_dimension > 0) {
+    x_size = min(x_size, max_texture_dimension);
+    y_size = min(y_size, max_texture_dimension);
+  }
+
+  // Finally, make sure the new sizes fit within the window, so we can
+  // use a parasite buffer.
+  int win_x_size = window->get_x_size();
+  if (win_x_size != 0 && x_size > win_x_size) {
+    x_size /= 2;
+    while (x_size > win_x_size) {
+      x_size /= 2;
+    }
+  }
+
+  int win_y_size = window->get_y_size();
+  if (win_y_size != 0 && y_size > win_y_size) {
+    y_size /= 2;
+    while (y_size > win_y_size) {
+      y_size /= 2;
+    }
   }
   }
 }
 }
 
 
@@ -394,7 +666,7 @@ make_texture_layer(const NodePath &render,
 
 
   NodePath geom;
   NodePath geom;
 
 
-  if (stage_info._stage->get_texcoord_name() == _target_stage->get_texcoord_name()) {
+  if (!_use_geom && stage_info._stage->get_texcoord_name() == _target_stage->get_texcoord_name()) {
     // If this TextureStage uses the target texcoords, we can just
     // If this TextureStage uses the target texcoords, we can just
     // generate a simple card the fills the entire buffer.
     // generate a simple card the fills the entire buffer.
     CardMaker cm(stage_info._tex->get_name());
     CardMaker cm(stage_info._tex->get_name());
@@ -405,18 +677,19 @@ make_texture_layer(const NodePath &render,
     geom = render.attach_new_node(cm.generate());
     geom = render.attach_new_node(cm.generate());
 
 
   } else {
   } else {
-    // If this TextureStage uses some other texcoords, we have to
-    // generate geometry that maps the texcoords to the target space.
-    // This will work only for very simple cases where the geometry is
-    // not too extensive and doesn't repeat over the same UV's.
+    // If this TextureStage uses some other texcoords (or if use_geom
+    // is true), we have to generate geometry that maps the texcoords
+    // to the target space.  This will work only for very simple cases
+    // where the geometry is not too extensive and doesn't repeat over
+    // the same UV's.
     PT(GeomNode) geom_node = new GeomNode(stage_info._tex->get_name());
     PT(GeomNode) geom_node = new GeomNode(stage_info._tex->get_name());
     transfer_geom(geom_node, stage_info._stage->get_texcoord_name(), 
     transfer_geom(geom_node, stage_info._stage->get_texcoord_name(), 
-                  geom_list);
+                  geom_list, false);
     
     
     geom = render.attach_new_node(geom_node);
     geom = render.attach_new_node(geom_node);
 
 
-    // Make sure we override the vertex color, so we don't pollute the
-    // texture with geometry color.
+    // Make sure we override the vertex color, so we don't pollute
+    // the texture with geometry color.
     geom.set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
     geom.set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
   }
   }
 
 
@@ -439,7 +712,8 @@ make_texture_layer(const NodePath &render,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MultitexReducer::
 void MultitexReducer::
 transfer_geom(GeomNode *geom_node, const TexCoordName *texcoord_name,
 transfer_geom(GeomNode *geom_node, const TexCoordName *texcoord_name,
-              const MultitexReducer::GeomList &geom_list) {
+              const MultitexReducer::GeomList &geom_list,
+              bool preserve_color) {
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
   for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
   for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
     const GeomInfo &geom_info = (*gi);
     const GeomInfo &geom_info = (*gi);
@@ -458,11 +732,26 @@ transfer_geom(GeomNode *geom_node, const TexCoordName *texcoord_name,
       }
       }
       
       
       geom->set_coords(coords, geom->get_texcoords_index(_target_stage->get_texcoord_name()));
       geom->set_coords(coords, geom->get_texcoords_index(_target_stage->get_texcoord_name()));
-      geom->set_texcoords(TexCoordName::get_default(),
-                          geom->get_texcoords_array(texcoord_name),
-                          geom->get_texcoords_index(texcoord_name));
+      if (texcoord_name != (const TexCoordName *)NULL) {
+        geom->set_texcoords(TexCoordName::get_default(),
+                            geom->get_texcoords_array(texcoord_name),
+                            geom->get_texcoords_index(texcoord_name));
+      }
+
+      CPT(RenderState) geom_state = RenderState::make_empty();
+      if (preserve_color) {
+        // Be sure to preserve whatever colors are on the geom.
+        const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_type());
+        if (ca != (const RenderAttrib *)NULL) {
+          geom_state->add_attrib(ca);
+        }
+        const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_type());
+        if (csa != (const RenderAttrib *)NULL) {
+          geom_state->add_attrib(csa);
+        }
+      }
       
       
-      geom_node->add_geom(geom);
+      geom_node->add_geom(geom, geom_state);
     }
     }
   }
   }
 }
 }

+ 23 - 6
panda/src/grutil/multitexReducer.h

@@ -26,6 +26,7 @@
 #include "texMatrixAttrib.h"
 #include "texMatrixAttrib.h"
 #include "transformState.h"
 #include "transformState.h"
 #include "geomNode.h"
 #include "geomNode.h"
+#include "nodePath.h"
 #include "luse.h"
 #include "luse.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pmap.h"
 #include "pmap.h"
@@ -58,10 +59,13 @@ PUBLISHED:
   ~MultitexReducer();
   ~MultitexReducer();
 
 
   void clear();
   void clear();
+  INLINE void scan(const NodePath &node, const NodePath &state_from = NodePath());
   void scan(PandaNode *node, const RenderState *state, 
   void scan(PandaNode *node, const RenderState *state, 
             const TransformState *transform);
             const TransformState *transform);
 
 
   void set_target(TextureStage *stage);
   void set_target(TextureStage *stage);
+  void set_use_geom(bool use_geom);
+  void set_allow_tex_mat(bool allow_tex_mat);
 
 
   void flatten(GraphicsOutput *window);
   void flatten(GraphicsOutput *window);
 
 
@@ -82,8 +86,11 @@ private:
 
 
   class GeomInfo {
   class GeomInfo {
   public:
   public:
-    INLINE GeomInfo(GeomNode *geom_node, int index);
+    INLINE GeomInfo(const RenderState *state, const RenderState *geom_net_state,
+                    GeomNode *geom_node, int index);
 
 
+    CPT(RenderState) _state;
+    CPT(RenderState) _geom_net_state;
     PT(GeomNode) _geom_node;
     PT(GeomNode) _geom_node;
     int _index;
     int _index;
   };
   };
@@ -94,6 +101,8 @@ private:
   Stages _stages;
   Stages _stages;
 
 
   PT(TextureStage) _target_stage;
   PT(TextureStage) _target_stage;
+  bool _use_geom;
+  bool _allow_tex_mat;
 
 
 private:
 private:
   void scan_geom_node(GeomNode *node, const RenderState *state, 
   void scan_geom_node(GeomNode *node, const RenderState *state, 
@@ -102,16 +111,24 @@ private:
   void record_stage_list(const StageList &stage_list, 
   void record_stage_list(const StageList &stage_list, 
                          const GeomInfo &geom_info);
                          const GeomInfo &geom_info);
 
 
-  void determine_size(int &x_size, int &y_size, int &aniso_degree,
-                      Texture::FilterType &minfilter, 
-                      Texture::FilterType &magfilter, 
-                      const MultitexReducer::StageList &stage_list) const;
+  size_t choose_model_stage(const StageList &stage_list) const;
+  bool determine_uv_range(TexCoordf &min_uv, TexCoordf &max_uv,
+                          const StageInfo &model_stage,
+                          const GeomList &geom_list) const;
+
+  void get_uv_scale(LVecBase2f &uv_scale, LVecBase2f &uv_trans,
+                    const TexCoordf &min_uv, const TexCoordf &max_uv) const;
+
+  void choose_texture_size(int &x_size, int &y_size,
+                           const StageInfo &model_stage,
+                           const LVecBase2f &uv_scale,
+                           GraphicsOutput *window) const;
 
 
   void make_texture_layer(const NodePath &render, 
   void make_texture_layer(const NodePath &render, 
                           const StageInfo &stage_info, 
                           const StageInfo &stage_info, 
                           const GeomList &geom_list);
                           const GeomList &geom_list);
   void transfer_geom(GeomNode *geom_node, const TexCoordName *texcoord_name,
   void transfer_geom(GeomNode *geom_node, const TexCoordName *texcoord_name,
-                     const MultitexReducer::GeomList &geom_list);
+                     const GeomList &geom_list, bool preserve_color);
 };
 };
 
 
 #include "multitexReducer.I"
 #include "multitexReducer.I"

+ 2 - 2
panda/src/testbed/pview.cxx

@@ -109,9 +109,9 @@ void
 event_0(CPT_Event event, void *) {
 event_0(CPT_Event event, void *) {
   // 0: run hacky test.
   // 0: run hacky test.
   MultitexReducer mr;
   MultitexReducer mr;
+  mr.set_use_geom(true);
 
 
-  NodePath models = framework.get_models();
-  mr.scan(models.node(), models.get_net_state(), models.get_net_transform());
+  mr.scan(framework.get_models());
 
 
   WindowFramework *wf = framework.get_window(0);
   WindowFramework *wf = framework.get_window(0);
   GraphicsWindow *win = wf->get_graphics_window();
   GraphicsWindow *win = wf->get_graphics_window();