Browse Source

initial experiments with keystone correction, revisit M_multisample transparency, robustify MultitexReducer

David Rose 21 years ago
parent
commit
b1375f32a4

+ 4 - 4
panda/src/display/graphicsBuffer.cxx

@@ -42,15 +42,15 @@ GraphicsBuffer(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
       << " using GSG " << (void *)gsg << "\n";
       << " using GSG " << (void *)gsg << "\n";
   }
   }
 
 
-  if (want_texture) {
-    setup_copy_texture(_name);
-  }
-
   _x_size = x_size;
   _x_size = x_size;
   _y_size = y_size;
   _y_size = y_size;
   _has_size = true;
   _has_size = true;
   _default_display_region->compute_pixels(_x_size, _y_size);
   _default_display_region->compute_pixels(_x_size, _y_size);
   _open_request = OR_none;
   _open_request = OR_none;
+
+  if (want_texture) {
+    setup_copy_texture(_name);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 19 - 1
panda/src/display/graphicsEngine.cxx

@@ -1196,7 +1196,7 @@ do_add_window(GraphicsOutput *window, GraphicsStateGuardian *gsg,
   _app.add_window(_app._window, window);
   _app.add_window(_app._window, window);
   
   
   display_cat.info()
   display_cat.info()
-    << "Created " << window->get_type() << "\n";
+    << "Created " << window->get_type() << " " << (void *)window << "\n";
   
   
   // By default, try to open each window as it is added.
   // By default, try to open each window as it is added.
   window->request_open();
   window->request_open();
@@ -1230,6 +1230,11 @@ do_remove_window(GraphicsOutput *window) {
   // If the window happened to be controlled by the app thread, we
   // If the window happened to be controlled by the app thread, we
   // might as well close it now rather than waiting for next frame.
   // might as well close it now rather than waiting for next frame.
   _app.do_pending(this);
   _app.do_pending(this);
+
+  if (display_cat.is_debug()) {
+    display_cat.debug()
+      << "Removed " << window->get_type() << " " << (void *)window << "\n";
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1450,6 +1455,19 @@ resort_windows() {
   _cdraw.sort();
   _cdraw.sort();
   _draw.sort();
   _draw.sort();
   _window.sort();
   _window.sort();
+
+  if (display_cat.is_debug()) {
+    display_cat.debug()
+      << "Windows resorted:";
+    Windows::const_iterator wi;
+    for (wi = _window.begin(); wi != _window.end(); ++wi) {
+      GraphicsOutput *win = (*wi);
+      display_cat.debug(false)
+        << " " << (void *)win;
+    }
+    display_cat.debug(false)
+      << "\n";
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 11 - 0
panda/src/display/graphicsOutput.cxx

@@ -164,6 +164,13 @@ setup_copy_texture(const string &name) {
   _texture->set_wrapu(Texture::WM_clamp);
   _texture->set_wrapu(Texture::WM_clamp);
   _texture->set_wrapv(Texture::WM_clamp);
   _texture->set_wrapv(Texture::WM_clamp);
 
 
+  if (has_size()) {
+    // If we know our size now, go ahead and tell the texture.
+    PixelBuffer *pb = _texture->_pbuffer;
+    pb->set_xsize(get_x_size());
+    pb->set_ysize(get_y_size());
+  }
+
   _copy_texture = true;
   _copy_texture = true;
 
 
   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
@@ -587,6 +594,10 @@ end_frame() {
   // directly into texture memory don't need to do this; they will set
   // directly into texture memory don't need to do this; they will set
   // _copy_texture to false.
   // _copy_texture to false.
   if (_copy_texture) {
   if (_copy_texture) {
+    if (display_cat.is_debug()) {
+      display_cat.debug()
+        << "Copying texture for " << (void *)this << " at frame end.\n";
+    }
     PStatTimer timer(_copy_texture_pcollector);
     PStatTimer timer(_copy_texture_pcollector);
     nassertv(has_texture());
     nassertv(has_texture());
     RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
     RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());

+ 14 - 0
panda/src/display/graphicsStateGuardian.I

@@ -168,6 +168,20 @@ get_copy_texture_inverted() const {
   return _copy_texture_inverted;
   return _copy_texture_inverted;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_generate_mipmap
+//       Access: Published
+//  Description: Returns true if this particular GSG can generate
+//               mipmaps for a texture automatically, or if they must
+//               be generated in software.  If this is true, then
+//               mipmaps can safely be enabled for rendered textures
+//               (e.g. using the MultitexReducer).
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_supports_generate_mipmap() const {
+  return _supports_generate_mipmap;
+}
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::set_scene
 //     Function: GraphicsStateGuardian::set_scene

+ 22 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -92,6 +92,10 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
   // Initially, we set this to false; a GSG that knows it has this
   // Initially, we set this to false; a GSG that knows it has this
   // property should set it to true.
   // property should set it to true.
   _copy_texture_inverted = false;
   _copy_texture_inverted = false;
+
+  // Similarly with these capabilities flags.
+  _supports_multisample = false;
+  _supports_generate_mipmap = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -103,6 +107,24 @@ GraphicsStateGuardian::
 ~GraphicsStateGuardian() {
 ~GraphicsStateGuardian() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_multisample
+//       Access: Published, Virtual
+//  Description: Returns true if this particular GSG supports using
+//               the multisample bits to provide antialiasing, and
+//               also supports M_multisample and M_multisample_mask
+//               transparency modes.  If this is not true for a
+//               particular GSG, Panda will map the M_multisample
+//               modes to M_binary.
+//
+//               This method is declared virtual solely so that it can
+//               be queried from cullResult.cxx.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+get_supports_multisample() const {
+  return _supports_multisample;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::reset
 //     Function: GraphicsStateGuardian::reset
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 4 - 0
panda/src/display/graphicsStateGuardian.h

@@ -84,6 +84,8 @@ PUBLISHED:
 
 
   INLINE int get_max_texture_stages() const;
   INLINE int get_max_texture_stages() const;
   INLINE bool get_copy_texture_inverted() const;
   INLINE bool get_copy_texture_inverted() const;
+  virtual bool get_supports_multisample() const;
+  INLINE bool get_supports_generate_mipmap() const;
 
 
 public:
 public:
   INLINE bool set_scene(SceneSetup *scene_setup);
   INLINE bool set_scene(SceneSetup *scene_setup);
@@ -280,6 +282,8 @@ protected:
   PT(PreparedGraphicsObjects) _prepared_objects;
   PT(PreparedGraphicsObjects) _prepared_objects;
   int _max_texture_stages;
   int _max_texture_stages;
   bool _copy_texture_inverted;
   bool _copy_texture_inverted;
+  bool _supports_multisample;
+  bool _supports_generate_mipmap;
 
 
 public:
 public:
   // Statistics
   // Statistics

+ 2 - 2
panda/src/display/parasiteBuffer.cxx

@@ -44,8 +44,6 @@ ParasiteBuffer(GraphicsOutput *host, const string &name,
       << " on " << _host->get_name() << "\n";
       << " on " << _host->get_name() << "\n";
   }
   }
 
 
-  setup_copy_texture(_name);
-
   _x_size = x_size;
   _x_size = x_size;
   _y_size = y_size;
   _y_size = y_size;
   _has_size = true;
   _has_size = true;
@@ -53,6 +51,8 @@ ParasiteBuffer(GraphicsOutput *host, const string &name,
 
 
   _is_valid = true;
   _is_valid = true;
 
 
+  setup_copy_texture(_name);
+
   nassertv(_x_size <= host->get_x_size() && _y_size <= host->get_y_size());
   nassertv(_x_size <= host->get_x_size() && _y_size <= host->get_y_size());
 }
 }
 
 

+ 0 - 1
panda/src/dxgsg7/dxGraphicsStateGuardian7.cxx

@@ -4590,7 +4590,6 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
     break;
     break;
 
 
   case TransparencyAttrib::M_alpha:
   case TransparencyAttrib::M_alpha:
-  case TransparencyAttrib::M_alpha_sorted:
   case TransparencyAttrib::M_multisample:
   case TransparencyAttrib::M_multisample:
   case TransparencyAttrib::M_multisample_mask:
   case TransparencyAttrib::M_multisample_mask:
   case TransparencyAttrib::M_dual:
   case TransparencyAttrib::M_dual:

+ 0 - 1
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -4316,7 +4316,6 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
     break;
     break;
 
 
   case TransparencyAttrib::M_alpha:
   case TransparencyAttrib::M_alpha:
-  case TransparencyAttrib::M_alpha_sorted:
   case TransparencyAttrib::M_multisample:
   case TransparencyAttrib::M_multisample:
   case TransparencyAttrib::M_multisample_mask:
   case TransparencyAttrib::M_multisample_mask:
   case TransparencyAttrib::M_dual:
   case TransparencyAttrib::M_dual:

+ 0 - 1
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -4319,7 +4319,6 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
     break;
     break;
 
 
   case TransparencyAttrib::M_alpha:
   case TransparencyAttrib::M_alpha:
-  case TransparencyAttrib::M_alpha_sorted:
   case TransparencyAttrib::M_multisample:
   case TransparencyAttrib::M_multisample:
   case TransparencyAttrib::M_multisample_mask:
   case TransparencyAttrib::M_multisample_mask:
   case TransparencyAttrib::M_dual:
   case TransparencyAttrib::M_dual:

+ 1 - 1
panda/src/express/ordered_vector.I

@@ -616,7 +616,7 @@ sort_unique() {
 template<class Key, class Compare>
 template<class Key, class Compare>
 INLINE void ordered_vector<Key, Compare>::
 INLINE void ordered_vector<Key, Compare>::
 sort_nonunique() {
 sort_nonunique() {
-  sort(begin(), end(), _compare);
+  stable_sort(begin(), end(), _compare);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 10 - 28
panda/src/glstuff/glGraphicsStateGuardian_src.I

@@ -191,23 +191,15 @@ enable_scissor(bool val)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void CLP(GraphicsStateGuardian)::
 INLINE void CLP(GraphicsStateGuardian)::
 enable_multisample_alpha_one(bool val) {
 enable_multisample_alpha_one(bool val) {
-  if (_multisample_alpha_one_enabled != val) {
-    _multisample_alpha_one_enabled = val;
-#ifdef GL_SAMPLE_ALPHA_TO_ONE_SGIS
-    if (val) {
-#ifdef GSG_VERBOSE
-      GLCAT.spam()
-        << "glEnable(GL_SAMPLE_ALPHA_TO_ONE_SGIS)" << endl;
-#endif
-      GLP(Enable)(GL_SAMPLE_ALPHA_TO_ONE_SGIS);
-    } else {
-#ifdef GSG_VERBOSE
-      GLCAT.spam()
-        << "glDisable(GL_SAMPLE_ALPHA_TO_ONE_SGIS)" << endl;
-#endif
-      GLP(Disable)(GL_SAMPLE_ALPHA_TO_ONE_SGIS);
+  if (_supports_multisample) {
+    if (_multisample_alpha_one_enabled != val) {
+      _multisample_alpha_one_enabled = val;
+      if (val) {
+        GLP(Enable)(GL_SAMPLE_ALPHA_TO_ONE);
+      } else {
+        GLP(Disable)(GL_SAMPLE_ALPHA_TO_ONE);
+      }
     }
     }
-#endif  // GL_SAMPLE_ALPHA_TO_ONE_SGIS
   }
   }
 }
 }
 
 
@@ -220,21 +212,11 @@ INLINE void CLP(GraphicsStateGuardian)::
 enable_multisample_alpha_mask(bool val) {
 enable_multisample_alpha_mask(bool val) {
   if (_multisample_alpha_mask_enabled != val) {
   if (_multisample_alpha_mask_enabled != val) {
     _multisample_alpha_mask_enabled = val;
     _multisample_alpha_mask_enabled = val;
-#ifdef GL_SAMPLE_ALPHA_TO_MASK_SGIS
     if (val) {
     if (val) {
-#ifdef GSG_VERBOSE
-      GLCAT.spam()
-        << "glEnable(GL_SAMPLE_ALPHA_TO_MASK_SGIS)" << endl;
-#endif
-      GLP(Enable)(GL_SAMPLE_ALPHA_TO_MASK_SGIS);
+      GLP(Enable)(GL_SAMPLE_ALPHA_TO_COVERAGE);
     } else {
     } else {
-#ifdef GSG_VERBOSE
-      GLCAT.spam()
-        << "glDisable(GL_SAMPLE_ALPHA_TO_MASK_SGIS)" << endl;
-#endif
-      GLP(Disable)(GL_SAMPLE_ALPHA_TO_MASK_SGIS);
+      GLP(Disable)(GL_SAMPLE_ALPHA_TO_COVERAGE);
     }
     }
-#endif  // GL_SAMPLE_ALPHA_TO_MASK_SGIS
   }
   }
 }
 }
 
 

+ 0 - 8
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -4345,15 +4345,7 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
     break;
     break;
     
     
   case TransparencyAttrib::M_alpha:
   case TransparencyAttrib::M_alpha:
-  case TransparencyAttrib::M_alpha_sorted:
   case TransparencyAttrib::M_dual:
   case TransparencyAttrib::M_dual:
-    // Should we really have an "alpha" and an "alpha_sorted"
-    // mode, like Performer does?  (The difference is that
-    // "alpha_sorted" is with the write to the depth buffer
-    // disabled.)  Or should we just use the separate depth write
-    // transition to control this?  Doing it implicitly requires a
-    // bit more logic here and in the state management; for now we
-    // require the user to explicitly turn off the depth write.
     enable_multisample_alpha_one(false);
     enable_multisample_alpha_one(false);
     enable_multisample_alpha_mask(false);
     enable_multisample_alpha_mask(false);
     enable_blend(true);
     enable_blend(true);

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

@@ -281,8 +281,6 @@ protected:
 
 
 public:
 public:
   bool _supports_bgr;
   bool _supports_bgr;
-  bool _supports_multisample;
-  bool _supports_generate_mipmap;
 
 
   bool _supports_multitexture;
   bool _supports_multitexture;
   PFNGLACTIVETEXTUREPROC _glActiveTexture;
   PFNGLACTIVETEXTUREPROC _glActiveTexture;

+ 82 - 16
panda/src/gobj/lens.cxx

@@ -105,6 +105,7 @@ clear() {
   _view_vector.set(0.0f, 1.0f, 0.0f);
   _view_vector.set(0.0f, 1.0f, 0.0f);
   _up_vector.set(0.0f, 0.0f, 1.0f);
   _up_vector.set(0.0f, 0.0f, 1.0f);
   _iod_offset = 0.0f;
   _iod_offset = 0.0f;
+  _keystone.set(0.0f, 0.0f, 0.0f);
   _user_flags = 0;
   _user_flags = 0;
   _comp_flags = CF_fov;
   _comp_flags = CF_fov;
 
 
@@ -574,6 +575,66 @@ get_view_mat() const {
   return _lens_mat;
   return _lens_mat;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Lens::clear_view_mat
+//       Access: Published
+//  Description: Resets the lens transform to identity.
+////////////////////////////////////////////////////////////////////
+void Lens::
+clear_view_mat() {
+  _lens_mat = LMatrix4f::ident_mat();
+  adjust_user_flags(0, UF_view_vector | UF_view_hpr | UF_iod_offset | UF_view_mat);
+  adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_lens_mat_inv | CF_view_hpr | CF_view_vector | CF_iod_offset,
+                    CF_lens_mat);
+  throw_change_event();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Lens::set_keystone
+//       Access: Published
+//  Description: Indicates the ratio of keystone correction to perform
+//               on the lens, in each of three axes.  This will build
+//               a special non-affine scale factor into the projection
+//               matrix that will compensate for keystoning of a
+//               projected image; this can be used to compensate for a
+//               projector that for physical reasons cannot be aimed
+//               directly at it screen.  The default value of 0, 0, 0
+//               indicates no keystone correction; specify a small
+//               value (usually in the range -1 .. 1) in one of the
+//               three axes to generate a keystone correction in that
+//               axis.
+////////////////////////////////////////////////////////////////////
+void Lens::
+set_keystone(const LVecBase3f &keystone) {
+  _keystone = keystone;
+  adjust_user_flags(0, UF_keystone);
+  adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_film_mat | CF_film_mat_inv, 0);
+  throw_change_event();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Lens::get_keystone
+//       Access: Published
+//  Description: Returns the direction in which the lens is facing.
+////////////////////////////////////////////////////////////////////
+const LVecBase3f &Lens::
+get_keystone() const {
+  return _keystone;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Lens::clear_keystone
+//       Access: Published
+//  Description: Resets the lens transform to identity.
+////////////////////////////////////////////////////////////////////
+void Lens::
+clear_keystone() {
+  _keystone = LVecBase3f(0.0f, 0.0f, 0.0f);
+  adjust_user_flags(UF_keystone, 0);
+  adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_film_mat | CF_film_mat_inv, 0);
+  throw_change_event();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Lens::set_frustum_from_corners
 //     Function: Lens::set_frustum_from_corners
 //       Access: Published
 //       Access: Published
@@ -1098,21 +1159,25 @@ extrude_impl(const LPoint3f &point2d, LPoint3f &near_point, LPoint3f &far_point)
   {
   {
     LVecBase4f full(point2d[0], point2d[1], -1.0f, 1.0f);
     LVecBase4f full(point2d[0], point2d[1], -1.0f, 1.0f);
     full = projection_mat_inv.xform(full);
     full = projection_mat_inv.xform(full);
-    if (full[3] == 0.0f) {
-      return false;
-    }
 
 
-    float recip_full3 = 1.0f/full[3];
-    near_point.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
+    float recip_full3 = 1.0f / max(full[3], 0.001f);
+    near_point.set(full[0] * recip_full3, 
+                   full[1] * recip_full3, 
+                   full[2] * recip_full3);
   }
   }
   {
   {
     LVecBase4f full(point2d[0], point2d[1], 1.0f, 1.0f);
     LVecBase4f full(point2d[0], point2d[1], 1.0f, 1.0f);
     full = projection_mat_inv.xform(full);
     full = projection_mat_inv.xform(full);
-    if (full[3] == 0.0f) {
-      return false;
-    }
-    float recip_full3 = 1.0f/full[3];
-    far_point.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
+
+    // We can truncate the weight factor at near 0.  If it goes too
+    // close to zero, or becomes negative, the far plane moves out
+    // past infinity and comes back in behind the lens, which is just
+    // crazy.  Truncating it to zero keeps the far plane from moving
+    // too far out.
+    float recip_full3 = 1.0f / max(full[3], 0.001f);
+    far_point.set(full[0] * recip_full3, 
+                  full[1] * recip_full3, 
+                  full[2] * recip_full3);
   }
   }
   return true;
   return true;
 }
 }
@@ -1376,12 +1441,6 @@ compute_film_mat() {
   LVecBase2f film_size = get_film_size();
   LVecBase2f film_size = get_film_size();
   LVector2f film_offset = get_film_offset();
   LVector2f film_offset = get_film_offset();
 
 
-  /* this line triggers a VC7 opt bug, so explicitly set matrix below instead
-  _film_mat =
-    LMatrix4f::translate_mat(-film_offset[0], -film_offset[1], 0.0f) *
-    LMatrix4f::scale_mat(2.0f / film_size[0], 2.0f / film_size[1], 1.0f);
-   */ 
-
   float scale_x = 2.0f / film_size[0];
   float scale_x = 2.0f / film_size[0];
   float scale_y = 2.0f / film_size[1];
   float scale_y = 2.0f / film_size[1];
   _film_mat.set(scale_x,      0.0f,   0.0f,  0.0f,
   _film_mat.set(scale_x,      0.0f,   0.0f,  0.0f,
@@ -1389,6 +1448,13 @@ compute_film_mat() {
                    0.0f,      0.0f,   1.0f,  0.0f,
                    0.0f,      0.0f,   1.0f,  0.0f,
         -film_offset[0] * scale_x, -film_offset[1] * scale_y, 0.0f,  1.0f);
         -film_offset[0] * scale_x, -film_offset[1] * scale_y, 0.0f,  1.0f);
 
 
+  if ((_user_flags & UF_keystone) != 0) {
+    _film_mat = LMatrix4f(csqrt(1.0f - _keystone[0] * _keystone[0]), 0.0f, 0.0f, _keystone[0],
+                          0.0f, csqrt(1.0f - _keystone[1] * _keystone[1]), 0.0f, _keystone[1],
+                          0.0f, 0.0f, csqrt(1.0f - _keystone[2] * _keystone[2]), _keystone[2],
+                          0.0f, 0.0f, 0.0f, 1.0f) * _film_mat;
+  }
+
   adjust_comp_flags(CF_film_mat_inv,
   adjust_comp_flags(CF_film_mat_inv,
                     CF_film_mat);
                     CF_film_mat);
 }
 }

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

@@ -110,6 +110,11 @@ PUBLISHED:
 
 
   void set_view_mat(const LMatrix4f &view_mat);
   void set_view_mat(const LMatrix4f &view_mat);
   const LMatrix4f &get_view_mat() const;
   const LMatrix4f &get_view_mat() const;
+  void clear_view_mat();
+
+  void set_keystone(const LVecBase3f &keystone);
+  const LVecBase3f &get_keystone() const;
+  void clear_keystone();
   
   
   // These flags are passed in as the last parameter to control the
   // These flags are passed in as the last parameter to control the
   // behavior of set_frustum_from_corners().  See the documentation
   // behavior of set_frustum_from_corners().  See the documentation
@@ -197,6 +202,7 @@ protected:
   LVecBase3f _view_hpr;
   LVecBase3f _view_hpr;
   LVector3f _view_vector, _up_vector;
   LVector3f _view_vector, _up_vector;
   float _iod_offset;
   float _iod_offset;
+  LVecBase3f _keystone;
 
 
   LMatrix4f _film_mat, _film_mat_inv;
   LMatrix4f _film_mat, _film_mat_inv;
   LMatrix4f _lens_mat, _lens_mat_inv;
   LMatrix4f _lens_mat, _lens_mat_inv;
@@ -214,6 +220,7 @@ protected:
     UF_view_vector         = 0x0080,
     UF_view_vector         = 0x0080,
     UF_iod_offset          = 0x0100,
     UF_iod_offset          = 0x0100,
     UF_view_mat            = 0x0200,
     UF_view_mat            = 0x0200,
+    UF_keystone            = 0x0400,
   };
   };
 
 
   enum CompFlags {
   enum CompFlags {

+ 2 - 1
panda/src/grutil/Sources.pp

@@ -24,7 +24,8 @@
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     cardMaker.I cardMaker.h \
     cardMaker.I cardMaker.h \
     frameRateMeter.I frameRateMeter.h \
     frameRateMeter.I frameRateMeter.h \
-    lineSegs.I lineSegs.h
+    lineSegs.I lineSegs.h \
+    multitexReducer.I multitexReducer.h
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

+ 14 - 3
panda/src/grutil/multitexReducer.cxx

@@ -418,11 +418,22 @@ scan_geom_node(GeomNode *node, const RenderState *state,
       int num_stages = ta->get_num_on_stages();
       int num_stages = ta->get_num_on_stages();
       for (int si = 0; si < num_stages; si++) {
       for (int si = 0; si < num_stages; si++) {
         TextureStage *stage = ta->get_on_stage(si);
         TextureStage *stage = ta->get_on_stage(si);
-        
-        stage_list.push_back(StageInfo(stage, ta, tma));
+        Texture *tex = ta->get_on_texture(stage);
+        PixelBuffer *tex_pbuffer = tex->_pbuffer;
+        if (tex_pbuffer != (PixelBuffer *)NULL &&
+            tex_pbuffer->get_xsize() != 0 &&
+            tex_pbuffer->get_ysize() != 0) {
+          stage_list.push_back(StageInfo(stage, ta, tma));
+
+        } else {
+          grutil_cat.info()
+            << "Ignoring invalid texture stage " << stage->get_name() << "\n";
+        }
       }
       }
 
 
-      record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
+      if (stage_list.size() >= 2) {
+        record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
+      }
     }
     }
   }
   }
 }
 }

+ 2 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -103,6 +103,8 @@ class Lens;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GraphicsStateGuardianBase : public TypedWritableReferenceCount {
 class EXPCL_PANDA GraphicsStateGuardianBase : public TypedWritableReferenceCount {
 public:
 public:
+  virtual bool get_supports_multisample() const=0;
+
   // These functions will be queried by the GeomIssuer to determine if
   // These functions will be queried by the GeomIssuer to determine if
   // it should issue normals, texcoords, and/or colors, based on the
   // it should issue normals, texcoords, and/or colors, based on the
   // GSG's current state.
   // GSG's current state.

+ 9 - 0
panda/src/pgraph/cullResult.cxx

@@ -97,6 +97,15 @@ add_object(CullableObject *object) {
       object->_state = state->compose(get_binary_state());
       object->_state = state->compose(get_binary_state());
       break;
       break;
 
 
+    case TransparencyAttrib::M_multisample:
+    case TransparencyAttrib::M_multisample_mask:
+      // The multisample modes are implemented using M_binary if the
+      // GSG in use doesn't support multisample.
+      if (!_gsg->get_supports_multisample()) {
+        object->_state = state->compose(get_binary_state());
+      }
+      break;
+
     case TransparencyAttrib::M_dual:
     case TransparencyAttrib::M_dual:
       if (m_dual) {
       if (m_dual) {
         // M_dual is implemented by drawing the opaque parts first,
         // M_dual is implemented by drawing the opaque parts first,

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

@@ -1371,7 +1371,6 @@ determine_bin_index() {
     if (trans != (const TransparencyAttrib *)NULL) {
     if (trans != (const TransparencyAttrib *)NULL) {
       switch (trans->get_mode()) {
       switch (trans->get_mode()) {
       case TransparencyAttrib::M_alpha:
       case TransparencyAttrib::M_alpha:
-      case TransparencyAttrib::M_alpha_sorted:
       case TransparencyAttrib::M_dual:
       case TransparencyAttrib::M_dual:
         // These transparency modes require special back-to-front sorting.
         // These transparency modes require special back-to-front sorting.
         bin_name = "transparent";
         bin_name = "transparent";

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

@@ -257,7 +257,7 @@ compare_to_impl(const RenderAttrib *other) const {
     }
     }
   }
   }
 
 
-  if (bi != _stages.end()) {
+  if (bi != ta->_stages.end()) {
     // a ran out first; b was longer.
     // a ran out first; b was longer.
     return -1;
     return -1;
   }
   }

+ 0 - 4
panda/src/pgraph/transparencyAttrib.cxx

@@ -68,10 +68,6 @@ output(ostream &out) const {
     out << "alpha";
     out << "alpha";
     break;
     break;
 
 
-  case M_alpha_sorted:
-    out << "alpha sorted";
-    break;
-
   case M_multisample:
   case M_multisample:
     out << "multisample";
     out << "multisample";
     break;
     break;

+ 9 - 9
panda/src/pgraph/transparencyAttrib.h

@@ -31,8 +31,8 @@ class FactoryParams;
 //               setting an alpha component to non-1 does not in
 //               setting an alpha component to non-1 does not in
 //               itself make an object transparent; you must also
 //               itself make an object transparent; you must also
 //               enable transparency mode with a suitable
 //               enable transparency mode with a suitable
-//               TransparencyTransition.  Similarly, it is wasteful to
-//               render an object with a TransparencyTransition in
+//               TransparencyAttrib.  Similarly, it is wasteful to
+//               render an object with a TransparencyAttrib in
 //               effect unless you actually want it to be at least
 //               effect unless you actually want it to be at least
 //               partially transparent (and it has alpha components
 //               partially transparent (and it has alpha components
 //               less than 1).
 //               less than 1).
@@ -40,13 +40,13 @@ class FactoryParams;
 class EXPCL_PANDA TransparencyAttrib : public RenderAttrib {
 class EXPCL_PANDA TransparencyAttrib : public RenderAttrib {
 PUBLISHED:
 PUBLISHED:
   enum Mode {
   enum Mode {
-    M_none,             // No transparency in effect.
-    M_alpha,            // Writes to depth buffer of transp objects disabled
-    M_alpha_sorted,     // Assumes transp objects are depth sorted
-    M_multisample,      // Source alpha values modified to 1.0 before writing
-    M_multisample_mask, // Source alpha values not modified
-    M_binary,           // Only writes pixels with alpha = 1.0
-    M_dual,             // 2-pass: draws opaque, then draws transparent
+    M_none,             // No transparency.
+    M_alpha,            // Normal transparency, panda will sort back-to-front.
+    M_notused,          // Unused placeholder.  Do not use this.
+    M_multisample,      // Uses ms buffer, alpha values modified to 1.0.
+    M_multisample_mask, // Uses ms buffer, alpha values not modified.
+    M_binary,           // Only writes pixels with alpha >= 0.5.
+    M_dual,             // opaque parts first, then sorted transparent parts.
   };
   };
 
 
 private:
 private:

+ 33 - 6
panda/src/testbed/pview.cxx

@@ -20,6 +20,7 @@
 #include "textNode.h"
 #include "textNode.h"
 #include "multitexReducer.h"
 #include "multitexReducer.h"
 #include "configVariableBool.h"
 #include "configVariableBool.h"
+#include "texturePool.h"
 
 
 #ifndef HAVE_GETOPT
 #ifndef HAVE_GETOPT
   #include "gnu_getopt.h"
   #include "gnu_getopt.h"
@@ -108,24 +109,50 @@ event_2(CPT_Event event, void *) {
 void
 void
 event_0(CPT_Event event, void *) {
 event_0(CPT_Event event, void *) {
   // 0: run hacky test.
   // 0: run hacky test.
-  static bool first = true;
+  static int count = 0;
 
 
-  if (first) {
+  static PT(TextureStage) ts;
+  if (ts == (TextureStage *)NULL) {
+    ts = new TextureStage("ts");
+  }
+
+  NodePath models = framework.get_models();
+
+  if (count == 0) {
     cerr << "applying scale\n";
     cerr << "applying scale\n";
-    framework.get_models().set_color_scale(0.7, 0.7, 0.1, 1.0);
-    first = false;
+    models.set_color_scale(0.7, 0.7, 0.1, 1.0);
 
 
   } else {
   } else {
     cerr << "flattening\n";
     cerr << "flattening\n";
     MultitexReducer mr;
     MultitexReducer mr;
     mr.set_use_geom(true);
     mr.set_use_geom(true);
-    
-    mr.scan(framework.get_models());
+    mr.scan(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();
     mr.flatten(win);
     mr.flatten(win);
+
+    if (count > 1) {
+      PT(Texture) tex = TexturePool::load_texture("maps/cmss12.rgb");
+      if (tex != (Texture *)NULL) {
+        cerr << "Reapplying\n";
+        
+        ts->set_mode(TextureStage::M_decal);
+        models.set_texture(ts, tex);
+     
+        if (count > 3 && (count % 2) == 1) {
+          cerr << "Reflattening\n";
+          MultitexReducer mr;
+          mr.scan(models);
+          
+          WindowFramework *wf = framework.get_window(0);
+          GraphicsWindow *win = wf->get_graphics_window();
+          mr.flatten(win);
+        }
+      }
+    }
   }
   }
+  count++;
 }
 }
 
 
 void 
 void