Browse Source

side-by-side stereo

David Rose 15 years ago
parent
commit
9124513b82

+ 21 - 6
direct/src/showbase/ShowBase.py

@@ -1084,10 +1084,11 @@ class ShowBase(DirectObject.DirectObject):
             win = self.win
 
         if win != None and win.hasSize():
-            if(win.getYSize() == 0 or win.getXSize() == 0):
-                #flub the aspect since we can't actually see anything
-                return 1
-            aspectRatio = float(win.getXSize()) / float(win.getYSize())
+            # Temporary hasattr for old Pandas
+            if not hasattr(win, 'getSbsLeftXSize'):
+                aspectRatio = float(win.getXSize()) / float(win.getYSize())
+            else:
+                aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize())
 
         else:
             if win == None or not hasattr(win, "getRequestedProperties"):
@@ -1405,6 +1406,16 @@ class ShowBase(DirectObject.DirectObject):
             name = win.getInputDeviceName(i)
             mk = self.dataRoot.attachNewNode(MouseAndKeyboard(win, i, name))
             mw = mk.attachNewNode(MouseWatcher("watcher%s" % (i)))
+
+            # Temporary hasattr for old Pandas
+            if hasattr(win, 'getSideBySideStereo') and win.getSideBySideStereo():
+                # If the window has side-by-side stereo enabled, then
+                # we should constrain the MouseWatcher to the window's
+                # DisplayRegion.  This will enable the MouseWatcher to
+                # track the left and right halves of the screen
+                # individually.
+                mw.node().setDisplayRegion(win.getOverlayDisplayRegion())
+                
             mb = mw.node().getModifierButtons()
             mb.addButton(KeyboardButton.shift())
             mb.addButton(KeyboardButton.control())
@@ -1438,7 +1449,7 @@ class ShowBase(DirectObject.DirectObject):
 
         aspectRatio = self.getAspectRatio()
         # Scale the smiley face to 32x32 pixels.
-        height = self.win.getYSize()
+        height = self.win.getSbsLeftYSize()
         lilsmiley.setScale(
             32.0 / height / aspectRatio,
             1.0, 32.0 / height)
@@ -2543,9 +2554,13 @@ class ShowBase(DirectObject.DirectObject):
                     # If anybody needs to update their GUI, put a callback on this event
                     messenger.send("aspectRatioChanged")
             
-            if win.getXSize() > 0 and win.getYSize() > 0:
+            # Temporary hasattr for old Pandas
+            if not hasattr(win, 'getSbsLeftXSize'):
                 self.pixel2d.setScale(2.0 / win.getXSize(), 1.0, 2.0 / win.getYSize())
                 self.pixel2dp.setScale(2.0 / win.getXSize(), 1.0, 2.0 / win.getYSize())
+            else:
+                self.pixel2d.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
+                self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
 
     def userExit(self):
         # The user has requested we exit the program.  Deal with this.

+ 20 - 0
panda/src/display/config_display.cxx

@@ -225,6 +225,26 @@ ConfigVariableString red_blue_stereo_colors
           "'green', 'cyan', 'magenta', 'yellow', or 'alpha', or a union "
           "of two or more words separated by a vertical pipe (|)."));
 
+ConfigVariableBool side_by_side_stereo
+("side-by-side-stereo", false,
+ PRC_DESC("Set this true to create windows with side-by-side stereo mode enabled "
+          "by default, if the framebuffer does not support true stereo "
+          "rendering."));
+
+ConfigVariableDouble sbs_left_dimensions
+("sbs-left-dimensions", "0.0 0.5 0.0 1.0",
+ PRC_DESC("Defines the default region of the window that is used for the "
+          "left eye, when side-by-side stereo is enabled.  This is a set of "
+          "four numbers, in the form left right top bottom, similar to a "
+          "normal DisplayRegion layout."));
+
+ConfigVariableDouble sbs_right_dimensions
+("sbs-right-dimensions", "0.5 1.0 0.0 1.0",
+ PRC_DESC("Defines the default region of the window that is used for the "
+          "right eye, when side-by-side stereo is enabled.  This is a set of "
+          "four numbers, in the form left right top bottom, similar to a "
+          "normal DisplayRegion layout."));
+
 ConfigVariableBool default_stereo_camera
 ("default-stereo-camera", true,
  PRC_DESC("When this is true, the default DisplayRegion created for "

+ 3 - 0
panda/src/display/config_display.h

@@ -59,6 +59,9 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableBool copy_texture_inverted;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool window_inverted;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool red_blue_stereo;
 extern EXPCL_PANDA_DISPLAY ConfigVariableString red_blue_stereo_colors;
+extern EXPCL_PANDA_DISPLAY ConfigVariableBool side_by_side_stereo;
+extern EXPCL_PANDA_DISPLAY ConfigVariableDouble sbs_left_dimensions;
+extern EXPCL_PANDA_DISPLAY ConfigVariableDouble sbs_right_dimensions;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool default_stereo_camera;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool color_scale_via_lighting;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool alpha_scale_via_texture;

+ 55 - 16
panda/src/display/displayRegion.I

@@ -48,10 +48,23 @@ get_lens_index() const {
 INLINE void DisplayRegion::
 get_dimensions(float &l, float &r, float &b, float &t) const {
   CDReader cdata(_cycler);
-  l = cdata->_l;
-  r = cdata->_r;
-  b = cdata->_b;
-  t = cdata->_t;
+  l = cdata->_dimensions[0];
+  r = cdata->_dimensions[1];
+  b = cdata->_dimensions[2];
+  t = cdata->_dimensions[3];
+}
+///////////////////////////////////////////////////////////////////
+
+//     Function: DisplayRegion::get_dimensions
+//       Access: Published
+//  Description: Retrieves the coordinates of the DisplayRegion's
+//               rectangle within its GraphicsOutput.  These numbers
+//               will be in the range [0..1].
+////////////////////////////////////////////////////////////////////
+INLINE LVecBase4f DisplayRegion::
+get_dimensions() const {
+  CDReader cdata(_cycler);
+  return cdata->_dimensions;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -64,7 +77,7 @@ get_dimensions(float &l, float &r, float &b, float &t) const {
 INLINE float DisplayRegion::
 get_left() const {
   CDReader cdata(_cycler);
-  return cdata->_l;
+  return cdata->_dimensions[0];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -77,7 +90,7 @@ get_left() const {
 INLINE float DisplayRegion::
 get_right() const {
   CDReader cdata(_cycler);
-  return cdata->_r;
+  return cdata->_dimensions[1];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -90,7 +103,7 @@ get_right() const {
 INLINE float DisplayRegion::
 get_bottom() const {
   CDReader cdata(_cycler);
-  return cdata->_b;
+  return cdata->_dimensions[2];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -103,7 +116,21 @@ get_bottom() const {
 INLINE float DisplayRegion::
 get_top() const {
   CDReader cdata(_cycler);
-  return cdata->_t;
+  return cdata->_dimensions[3];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::set_dimensions
+//       Access: Published, Virtual
+//  Description: Changes the portion of the framebuffer this
+//               DisplayRegion corresponds to.  The parameters range
+//               from 0 to 1, where 0,0 is the lower left corner and
+//               1,1 is the upper right; (0, 1, 0, 1) represents the
+//               whole screen.
+////////////////////////////////////////////////////////////////////
+INLINE void DisplayRegion::
+set_dimensions(float l, float r, float b, float t) {
+  set_dimensions(LVecBase4f(l, r, b, t));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -586,10 +613,22 @@ is_any_clear_active() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void DisplayRegionPipelineReader::
 get_dimensions(float &l, float &r, float &b, float &t) const {
-  l = _cdata->_l;
-  r = _cdata->_r;
-  b = _cdata->_b;
-  t = _cdata->_t;
+  l = _cdata->_dimensions[0];
+  r = _cdata->_dimensions[1];
+  b = _cdata->_dimensions[2];
+  t = _cdata->_dimensions[3];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionPipelineReader::get_dimensions
+//       Access: Public
+//  Description: Retrieves the coordinates of the DisplayRegion's
+//               rectangle within its GraphicsOutput.  These numbers
+//               will be in the range [0..1].
+////////////////////////////////////////////////////////////////////
+INLINE const LVecBase4f &DisplayRegionPipelineReader::
+get_dimensions() const {
+  return _cdata->_dimensions;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -601,7 +640,7 @@ get_dimensions(float &l, float &r, float &b, float &t) const {
 ////////////////////////////////////////////////////////////////////
 INLINE float DisplayRegionPipelineReader::
 get_left() const {
-  return _cdata->_l;
+  return _cdata->_dimensions[0];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -613,7 +652,7 @@ get_left() const {
 ////////////////////////////////////////////////////////////////////
 INLINE float DisplayRegionPipelineReader::
 get_right() const {
-  return _cdata->_r;
+  return _cdata->_dimensions[1];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -625,7 +664,7 @@ get_right() const {
 ////////////////////////////////////////////////////////////////////
 INLINE float DisplayRegionPipelineReader::
 get_bottom() const {
-  return _cdata->_b;
+  return _cdata->_dimensions[2];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -637,7 +676,7 @@ get_bottom() const {
 ////////////////////////////////////////////////////////////////////
 INLINE float DisplayRegionPipelineReader::
 get_top() const {
-  return _cdata->_t;
+  return _cdata->_dimensions[3];
 }
 
 ////////////////////////////////////////////////////////////////////

+ 17 - 24
panda/src/display/displayRegion.cxx

@@ -32,7 +32,7 @@ TypeHandle DisplayRegionPipelineReader::_type_handle;
 //  Description:
 ////////////////////////////////////////////////////////////////////
 DisplayRegion::
-DisplayRegion(GraphicsOutput *window, float l, float r, float b, float t) :
+DisplayRegion(GraphicsOutput *window, const LVecBase4f &dimensions) :
   _window(window),
   _incomplete_render(true),
   _texture_reload_priority(0),
@@ -41,7 +41,7 @@ DisplayRegion(GraphicsOutput *window, float l, float r, float b, float t) :
 {
   _screenshot_buffer_type = window->get_draw_buffer_type();
   _draw_buffer_type = window->get_draw_buffer_type();
-  set_dimensions(l, r, b, t);
+  set_dimensions(dimensions);
   compute_pixels_all_stages();
 }
 
@@ -124,15 +124,12 @@ set_lens_index(int index) {
 //               whole screen.
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
-set_dimensions(float l, float r, float b, float t) {
+set_dimensions(const LVecBase4f &dimensions) {
   int pipeline_stage = Thread::get_current_pipeline_stage();
   nassertv(pipeline_stage == 0);
   CDWriter cdata(_cycler);
 
-  cdata->_l = l;
-  cdata->_r = r;
-  cdata->_b = b;
-  cdata->_t = t;
+  cdata->_dimensions = dimensions;
 
   if (_window != (GraphicsOutput *)NULL && _window->has_size()) {
     do_compute_pixels(_window->get_fb_x_size(), _window->get_fb_y_size(), cdata);
@@ -382,8 +379,7 @@ set_cube_map_index(int cube_map_index) {
 void DisplayRegion::
 output(ostream &out) const {
   CDReader cdata(_cycler);
-  out << "DisplayRegion(" << cdata->_l << " " << cdata->_r << " "
-      << cdata->_b << " " << cdata->_t << ")=pixels(" << cdata->_pl
+  out << "DisplayRegion(" << cdata->_dimensions << ")=pixels(" << cdata->_pl
       << " " << cdata->_pr << " " << cdata->_pb << " " << cdata->_pt
       << ")";
 }
@@ -699,23 +695,23 @@ do_compute_pixels(int x_size, int y_size, CData *cdata) {
       << "DisplayRegion::do_compute_pixels(" << x_size << ", " << y_size << ")\n";
   }
 
-  cdata->_pl = int((cdata->_l * x_size) + 0.5);
-  cdata->_pr = int((cdata->_r * x_size) + 0.5);
+  cdata->_pl = int((cdata->_dimensions[0] * x_size) + 0.5);
+  cdata->_pr = int((cdata->_dimensions[1] * x_size) + 0.5);
 
   nassertv(_window != (GraphicsOutput *)NULL);
   if (_window->get_inverted()) {
     // The window is inverted; compute the DisplayRegion accordingly.
-    cdata->_pb = int(((1.0f - cdata->_t) * y_size) + 0.5);
-    cdata->_pt = int(((1.0f - cdata->_b) * y_size) + 0.5);
-    cdata->_pbi = int((cdata->_t * y_size) + 0.5);
-    cdata->_pti = int((cdata->_b * y_size) + 0.5);
+    cdata->_pb = int(((1.0f - cdata->_dimensions[3]) * y_size) + 0.5);
+    cdata->_pt = int(((1.0f - cdata->_dimensions[2]) * y_size) + 0.5);
+    cdata->_pbi = int((cdata->_dimensions[3] * y_size) + 0.5);
+    cdata->_pti = int((cdata->_dimensions[2] * y_size) + 0.5);
 
   } else {
     // The window is normal.
-    cdata->_pb = int((cdata->_b * y_size) + 0.5);
-    cdata->_pt = int((cdata->_t * y_size) + 0.5);
-    cdata->_pbi = int(((1.0f - cdata->_b) * y_size) + 0.5);
-    cdata->_pti = int(((1.0f - cdata->_t) * y_size) + 0.5);
+    cdata->_pb = int((cdata->_dimensions[2] * y_size) + 0.5);
+    cdata->_pt = int((cdata->_dimensions[3] * y_size) + 0.5);
+    cdata->_pbi = int(((1.0f - cdata->_dimensions[2]) * y_size) + 0.5);
+    cdata->_pti = int(((1.0f - cdata->_dimensions[3]) * y_size) + 0.5);
   }
 }
 
@@ -750,7 +746,7 @@ set_active_index(int index) {
 ////////////////////////////////////////////////////////////////////
 DisplayRegion::CData::
 CData() :
-  _l(0.), _r(1.), _b(0.), _t(1.),
+  _dimensions(0.0f, 1.0f, 0.0f, 1.0f),
   _pl(0), _pr(0), _pb(0), _pt(0),
   _pbi(0), _pti(0), _lens_index(0),
   _camera_node((Camera *)NULL),
@@ -768,10 +764,7 @@ CData() :
 ////////////////////////////////////////////////////////////////////
 DisplayRegion::CData::
 CData(const DisplayRegion::CData &copy) :
-  _l(copy._l),
-  _r(copy._r),
-  _b(copy._b),
-  _t(copy._t),
+  _dimensions(copy._dimensions),
   _pl(copy._pl),
   _pr(copy._pr),
   _pb(copy._pb),

+ 7 - 7
panda/src/display/displayRegion.h

@@ -37,6 +37,7 @@
 #include "pStatCollector.h"
 #include "cullTraverser.h"
 #include "callbackObject.h"
+#include "luse.h"
 
 class GraphicsOutput;
 class GraphicsPipe;
@@ -58,8 +59,7 @@ class CullTraverser;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_DISPLAY DisplayRegion : public DisplayRegionBase, public DrawableRegion {
 protected:
-  DisplayRegion(GraphicsOutput *window,
-                float l, float r, float b, float t);
+  DisplayRegion(GraphicsOutput *window, const LVecBase4f &dimensions);
 
 private:
   DisplayRegion(const DisplayRegion &copy);
@@ -73,11 +73,13 @@ public:
 
 PUBLISHED:
   INLINE void get_dimensions(float &l, float &r, float &b, float &t) const;
+  INLINE LVecBase4f get_dimensions() const;
   INLINE float get_left() const;
   INLINE float get_right() const;
   INLINE float get_bottom() const;
   INLINE float get_top() const;
-  virtual void set_dimensions(float l, float r, float b, float t);
+  INLINE void set_dimensions(float l, float r, float b, float t);
+  virtual void set_dimensions(const LVecBase4f &dimensions);
 
   INLINE GraphicsOutput *get_window() const;
   GraphicsPipe *get_pipe() const;
@@ -184,10 +186,7 @@ private:
       return DisplayRegion::get_class_type();
     }
 
-    float _l;
-    float _r;
-    float _b;
-    float _t;
+    LVecBase4f _dimensions;  // left, right, bottom, top
     
     int _pl;
     int _pr;
@@ -284,6 +283,7 @@ public:
   INLINE bool is_any_clear_active() const;
 
   INLINE void get_dimensions(float &l, float &r, float &b, float &t) const;
+  INLINE const LVecBase4f &get_dimensions() const;
   INLINE float get_left() const;
   INLINE float get_right() const;
   INLINE float get_bottom() const;

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

@@ -30,7 +30,7 @@ GraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
                const WindowProperties &win_prop, int flags,
                GraphicsStateGuardian *gsg,
                GraphicsOutput *host) :
-  GraphicsOutput(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
+  GraphicsOutput(engine, pipe, name, fb_prop, win_prop, flags, gsg, host, false)
 {
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, this);
@@ -41,7 +41,7 @@ GraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
       << "Creating new offscreen buffer " << get_name() << "\n";
   }
 
-  _default_display_region->compute_pixels(_x_size, _y_size);
+  _overlay_display_region->compute_pixels(_x_size, _y_size);
   _open_request = OR_none;
 }
 

+ 179 - 6
panda/src/display/graphicsOutput.I

@@ -209,6 +209,66 @@ get_fb_y_size() const {
   return max(int(_y_size * get_pixel_factor()), 1);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_sbs_left_x_size
+//       Access: Published
+//  Description: If side-by-side stereo is enabled, this returns the
+//               pixel width of the left eye, based on scaling
+//               get_x_size() by get_sbs_left_dimensions().  If
+//               side-by-side stereo is not enabled, this returns the
+//               same as get_x_size().
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsOutput::
+get_sbs_left_x_size() const {
+  float left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0];
+  return max(int(_x_size * left_w), 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_sbs_left_y_size
+//       Access: Published
+//  Description: If side-by-side stereo is enabled, this returns the
+//               pixel height of the left eye, based on scaling
+//               get_y_size() by get_sbs_left_dimensions().  If
+//               side-by-side stereo is not enabled, this returns the
+//               same as get_y_size().
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsOutput::
+get_sbs_left_y_size() const {
+  float left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2];
+  return max(int(_y_size * left_h), 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_sbs_right_x_size
+//       Access: Published
+//  Description: If side-by-side stereo is enabled, this returns the
+//               pixel width of the right eye, based on scaling
+//               get_x_size() by get_sbs_right_dimensions().  If
+//               side-by-side stereo is not enabled, this returns the
+//               same as get_x_size().
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsOutput::
+get_sbs_right_x_size() const {
+  float right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0];
+  return max(int(_x_size * right_w), 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_sbs_right_y_size
+//       Access: Published
+//  Description: If side-by-side stereo is enabled, this returns the
+//               pixel height of the right eye, based on scaling
+//               get_y_size() by get_sbs_right_dimensions().  If
+//               side-by-side stereo is not enabled, this returns the
+//               same as get_y_size().
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsOutput::
+get_sbs_right_y_size() const {
+  float right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2];
+  return max(int(_y_size * right_h), 1);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::has_size
 //       Access: Published
@@ -355,6 +415,43 @@ get_right_eye_color_mask() const {
   return _right_eye_color_mask;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_side_by_side_stereo
+//       Access: Published
+//  Description: Returns whether side-by-side stereo mode is in effect for
+//               this particular window.  See set_side_by_side_stereo().
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsOutput::
+get_side_by_side_stereo() const {
+  return _side_by_side_stereo;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_sbs_left_dimensions
+//       Access: Published
+//  Description: Returns the effective sub-region of the window for
+//               displaying the left channel, if side-by-side stereo
+//               mode is in effect for the window.  See
+//               set_side_by_side_stereo().
+////////////////////////////////////////////////////////////////////
+INLINE const LVecBase4f &GraphicsOutput::
+get_sbs_left_dimensions() const {
+  return _sbs_left_dimensions;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_sbs_right_dimensions
+//       Access: Published
+//  Description: Returns the effective sub-region of the window for
+//               displaying the right channel, if side-by-side stereo
+//               mode is in effect for the window.  See
+//               set_side_by_side_stereo().
+////////////////////////////////////////////////////////////////////
+INLINE const LVecBase4f &GraphicsOutput::
+get_sbs_right_dimensions() const {
+  return _sbs_right_dimensions;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::get_fb_properties
 //       Access: Published
@@ -375,7 +472,7 @@ get_fb_properties() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GraphicsOutput::
 is_stereo() const {
-  return _red_blue_stereo || _fb_properties.is_stereo();
+  return _red_blue_stereo || _side_by_side_stereo || _fb_properties.is_stereo();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -505,20 +602,61 @@ make_display_region() {
   return make_display_region(0.0f, 1.0f, 0.0f, 1.0f);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::make_display_region
+//       Access: Published
+//  Description: Creates a new DisplayRegion that covers the indicated
+//               sub-rectangle within the window.  The range on all
+//               parameters is 0..1.
+//
+//               If is_stereo() is true for this window, and
+//               default-stereo-camera is configured true, this
+//               actually makes a StereoDisplayRegion.  Call
+//               make_mono_display_region() or
+//               make_stereo_display_region() if you want to insist on
+//               one or the other.
+////////////////////////////////////////////////////////////////////
+DisplayRegion *GraphicsOutput::
+make_display_region(float l, float r, float b, float t) {
+  return make_display_region(LVecBase4f(l, r, b, t));
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::make_mono_display_region
 //       Access: Published
 //  Description: Creates a new DisplayRegion that covers the entire
 //               window.
 //
-//               This always returns a mono DisplayRegion, even if
-//               is_stereo() is true.
+//               This generally returns a mono DisplayRegion, even if
+//               is_stereo() is true.  However, if side-by-side stereo
+//               is enabled, this will return a StereoDisplayRegion
+//               whose two eyes are both set to SC_mono.  (This is
+//               necessary because in side-by-side stereo mode, it is
+//               necessary to draw even mono DisplayRegions twice).
 ////////////////////////////////////////////////////////////////////
 INLINE DisplayRegion *GraphicsOutput::
 make_mono_display_region() {
   return make_mono_display_region(0.0f, 1.0f, 0.0f, 1.0f);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::make_mono_display_region
+//       Access: Published
+//  Description: Creates a new DisplayRegion that covers the entire
+//               window.
+//
+//               This generally returns a mono DisplayRegion, even if
+//               is_stereo() is true.  However, if side-by-side stereo
+//               is enabled, this will return a StereoDisplayRegion
+//               whose two eyes are both set to SC_mono.  (This is
+//               necessary because in side-by-side stereo mode, it is
+//               necessary to draw even mono DisplayRegions twice).
+////////////////////////////////////////////////////////////////////
+INLINE DisplayRegion *GraphicsOutput::
+make_mono_display_region(float l, float r, float b, float t) {
+  return make_mono_display_region(LVecBase4f(l, r, b, t));
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::make_stereo_display_region
 //       Access: Published
@@ -533,6 +671,41 @@ make_stereo_display_region() {
   return make_stereo_display_region(0.0f, 1.0f, 0.0f, 1.0f);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::make_stereo_display_region
+//       Access: Published
+//  Description: Creates a new DisplayRegion that covers the entire
+//               window.
+//
+//               This always returns a stereo DisplayRegion, even if
+//               is_stereo() is false.
+////////////////////////////////////////////////////////////////////
+INLINE StereoDisplayRegion *GraphicsOutput::
+make_stereo_display_region(float l, float r, float b, float t) {
+  return make_stereo_display_region(LVecBase4f(l, r, b, t));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_overlay_display_region
+//       Access: Published
+//  Description: Returns the special "overlay" DisplayRegion that is
+//               created for each window or buffer.  This
+//               DisplayRegion covers the entire window, but cannot be
+//               used for rendering.  It is a placeholder only, to
+//               indicate the dimensions of the window, and is usually
+//               used internally for purposes such as clearing the
+//               window, or grabbing a screenshot of the window.
+//
+//               There are very few applications that require access
+//               to this DisplayRegion.  Normally, you should create
+//               your own DisplayRegion that covers the window, if you
+//               want to render to the window.
+////////////////////////////////////////////////////////////////////
+INLINE DisplayRegion *GraphicsOutput::
+get_overlay_display_region() const {
+  return _overlay_display_region;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::make_screenshot_filename
 //       Access: Published, Static
@@ -563,7 +736,7 @@ make_screenshot_filename(const string &prefix) {
 ////////////////////////////////////////////////////////////////////
 INLINE Filename GraphicsOutput::
 save_screenshot_default(const string &prefix) {
-  return _default_display_region->save_screenshot_default(prefix);
+  return _overlay_display_region->save_screenshot_default(prefix);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -578,7 +751,7 @@ save_screenshot_default(const string &prefix) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GraphicsOutput::
 save_screenshot(const Filename &filename, const string &image_comment) {
-  return _default_display_region->save_screenshot(filename, image_comment);
+  return _overlay_display_region->save_screenshot(filename, image_comment);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -590,7 +763,7 @@ save_screenshot(const Filename &filename, const string &image_comment) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GraphicsOutput::
 get_screenshot(PNMImage &image) {
-  return _default_display_region->get_screenshot(image);
+  return _overlay_display_region->get_screenshot(image);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 481 - 289
panda/src/display/graphicsOutput.cxx

@@ -74,7 +74,8 @@ GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
                const WindowProperties &win_prop,
                int flags,
                GraphicsStateGuardian *gsg,
-               GraphicsOutput *host) :
+               GraphicsOutput *host,
+               bool default_stereo_flags) :
   _lock("GraphicsOutput"),
   _cull_window_pcollector(_cull_pcollector, name),
   _draw_window_pcollector(_draw_pcollector, name)
@@ -109,6 +110,9 @@ GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
   _red_blue_stereo = false;
   _left_eye_color_mask = 0x0f;
   _right_eye_color_mask = 0x0f;
+  _side_by_side_stereo = false;
+  _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
+  _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
   _delete_flag = false;
   _texture_card = 0;
   _trigger_copy = false;
@@ -119,11 +123,28 @@ GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
     _draw_buffer_type = RenderBuffer::T_back;
   }
 
+  if (default_stereo_flags) {
+    // Check the config variables to see if we should make this a
+    // "stereo" buffer or window.
+    _red_blue_stereo = red_blue_stereo && !fb_prop.is_stereo();
+    if (_red_blue_stereo) {
+      _left_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(0));
+      _right_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(1));
+    }
+    _side_by_side_stereo = side_by_side_stereo && !fb_prop.is_stereo();
+    if (_side_by_side_stereo) {
+      _sbs_left_dimensions.set(sbs_left_dimensions[0], sbs_left_dimensions[1], 
+                               sbs_left_dimensions[2], sbs_left_dimensions[3]);
+      _sbs_right_dimensions.set(sbs_right_dimensions[0], sbs_right_dimensions[1], 
+                                sbs_right_dimensions[2], sbs_right_dimensions[3]);
+    }
+  }
+
   // We start out with one DisplayRegion that covers the whole window,
   // which we may use internally for full-window operations like
   // clear() and get_screenshot().
-  _default_display_region = make_mono_display_region(0.0f, 1.0f, 0.0f, 1.0f);
-  _default_display_region->set_active(false);
+  _overlay_display_region = make_mono_display_region(0.0f, 1.0f, 0.0f, 1.0f);
+  _overlay_display_region->set_active(false);
 
   _display_regions_stale = false;
 
@@ -206,7 +227,7 @@ GraphicsOutput::
 
   _total_display_regions.clear();
   _active_display_regions.clear();
-  _default_display_region = NULL;
+  _overlay_display_region = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -442,6 +463,64 @@ set_inverted(bool inverted) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::set_side_by_side_stereo
+//       Access: Published
+//  Description: Enables side-by-side stereo mode on this particular
+//               window.  When side-by-side stereo mode is in effect,
+//               DisplayRegions that have the "left" channel set will
+//               render on the part of the window specified by
+//               sbs_left_dimensions (typically the left half: (0,
+//               0.5, 0, 1)), while DisplayRegions that have the
+//               "right" channel set will render on the part of the
+//               window specified by sbs_right_dimensions (typically
+//               the right half: (0.5, 1, 0, 1)).
+//
+//               This is commonly used in a dual-monitor mode, where a
+//               window is opened that spans two monitors, and each
+//               monitor represents a different eye.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+set_side_by_side_stereo(bool side_by_side_stereo) {
+  LVecBase4f left, right;
+  left.set(sbs_left_dimensions[0], sbs_left_dimensions[1], 
+           sbs_left_dimensions[2], sbs_left_dimensions[3]);
+  right.set(sbs_right_dimensions[0], sbs_right_dimensions[1], 
+            sbs_right_dimensions[2], sbs_right_dimensions[3]);
+  set_side_by_side_stereo(side_by_side_stereo, left, right);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::set_side_by_side_stereo
+//       Access: Published
+//  Description: Enables side-by-side stereo mode on this particular
+//               window.  When side-by-side stereo mode is in effect,
+//               DisplayRegions that have the "left" channel set will
+//               render on the part of the window specified by
+//               sbs_left_dimensions (typically the left half: (0,
+//               0.5, 0, 1)), while DisplayRegions that have the
+//               "right" channel set will render on the part of the
+//               window specified by sbs_right_dimensions (typically
+//               the right half: (0.5, 1, 0, 1)).
+//
+//               This is commonly used in a dual-monitor mode, where a
+//               window is opened that spans two monitors, and each
+//               monitor represents a different eye.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+set_side_by_side_stereo(bool side_by_side_stereo,
+                        const LVecBase4f &sbs_left_dimensions,
+                        const LVecBase4f &sbs_right_dimensions) {
+  _side_by_side_stereo = side_by_side_stereo;
+  if (_side_by_side_stereo) {
+    _sbs_left_dimensions = sbs_left_dimensions;
+    _sbs_right_dimensions = sbs_right_dimensions;
+  } else {
+    _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
+    _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::set_sort
 //       Access: Published, Virtual
@@ -473,11 +552,11 @@ set_sort(int sort) {
 //               one or the other.
 ////////////////////////////////////////////////////////////////////
 DisplayRegion *GraphicsOutput::
-make_display_region(float l, float r, float b, float t) {
+make_display_region(const LVecBase4f &dimensions) {
   if (is_stereo() && default_stereo_camera) {
-    return make_stereo_display_region(l, r, b, t);
+    return make_stereo_display_region(dimensions);
   } else {
-    return make_mono_display_region(l, r, b, t);
+    return make_mono_display_region(dimensions);
   }
 }
 
@@ -488,12 +567,23 @@ make_display_region(float l, float r, float b, float t) {
 //               sub-rectangle within the window.  The range on all
 //               parameters is 0..1.
 //
-//               This always returns a mono DisplayRegion, even if
-//               is_stereo() is true.
+//               This generally returns a mono DisplayRegion, even if
+//               is_stereo() is true.  However, if side-by-side stereo
+//               is enabled, this will return a StereoDisplayRegion
+//               whose two eyes are both set to SC_mono.  (This is
+//               necessary because in side-by-side stereo mode, it is
+//               necessary to draw even mono DisplayRegions twice).
 ////////////////////////////////////////////////////////////////////
 DisplayRegion *GraphicsOutput::
-make_mono_display_region(float l, float r, float b, float t) {
-  return add_display_region(new DisplayRegion(this, l, r, b, t));
+make_mono_display_region(const LVecBase4f &dimensions) {
+  if (_side_by_side_stereo) {
+    StereoDisplayRegion *dr = make_stereo_display_region(dimensions);
+    dr->get_left_eye()->set_stereo_channel(Lens::SC_mono);
+    dr->get_right_eye()->set_stereo_channel(Lens::SC_mono);
+    return dr;
+  }
+
+  return add_display_region(new DisplayRegion(this, dimensions));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -507,11 +597,40 @@ make_mono_display_region(float l, float r, float b, float t) {
 //               is_stereo() is false.
 ////////////////////////////////////////////////////////////////////
 StereoDisplayRegion *GraphicsOutput::
-make_stereo_display_region(float l, float r, float b, float t) {
-  PT(DisplayRegion) left = new DisplayRegion(this, l, r, b, t);
-  PT(DisplayRegion) right = new DisplayRegion(this, l, r, b, t);
+make_stereo_display_region(const LVecBase4f &dimensions) {
+  PT(DisplayRegion) left, right;
+
+  if (_side_by_side_stereo) {
+    // On a side-by-side stereo window, each eye gets the
+    // corresponding dimensions of its own sub-region.
+    float left_l = _sbs_left_dimensions[0];
+    float left_b = _sbs_left_dimensions[2];
+    float left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0];
+    float left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2];
+    LVecBase4f left_dimensions(dimensions[0] * left_w + left_l,
+                               dimensions[1] * left_w + left_l,
+                               dimensions[2] * left_h + left_b,
+                               dimensions[3] * left_h + left_b);
+    left = new DisplayRegion(this, left_dimensions);
+
+    float right_l = _sbs_right_dimensions[0];
+    float right_b = _sbs_right_dimensions[2];
+    float right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0];
+    float right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2];
+    LVecBase4f right_dimensions(dimensions[0] * right_w + right_l,
+                                dimensions[1] * right_w + right_l,
+                                dimensions[2] * right_h + right_b,
+                                dimensions[3] * right_h + right_b);
+    right = new DisplayRegion(this, right_dimensions);
+
+  } else {
+    // Not a side-by-side stereo window; thus, both the left and right
+    // eyes are the same region: the region specified.
+    left = new DisplayRegion(this, dimensions);
+    right = new DisplayRegion(this, dimensions);
+  }
 
-  PT(StereoDisplayRegion) stereo = new StereoDisplayRegion(this, l, r, b, t,
+  PT(StereoDisplayRegion) stereo = new StereoDisplayRegion(this, dimensions,
                                                            left, right);
   add_display_region(stereo);
   add_display_region(left);
@@ -533,7 +652,7 @@ bool GraphicsOutput::
 remove_display_region(DisplayRegion *display_region) {
   LightMutexHolder holder(_lock);
 
-  nassertr(display_region != _default_display_region, false);
+  nassertr(display_region != _overlay_display_region, false);
 
   if (display_region->is_stereo()) {
     StereoDisplayRegion *sdr;
@@ -560,17 +679,42 @@ remove_all_display_regions() {
        dri != _total_display_regions.end();
        ++dri) {
     DisplayRegion *display_region = (*dri);
-    if (display_region != _default_display_region) {
+    if (display_region != _overlay_display_region) {
       // Let's aggressively clean up the display region too.
       display_region->cleanup();
       display_region->_window = NULL;
     }
   }
   _total_display_regions.clear();
-  _total_display_regions.push_back(_default_display_region);
+  _total_display_regions.push_back(_overlay_display_region);
   _display_regions_stale = true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::set_overlay_display_region
+//       Access: Published
+//  Description: Replaces the special "overlay" DisplayRegion that is
+//               created for each window or buffer.  See
+//               get_overlay_display_region().  This must be a new
+//               DisplayRegion that has already been created for this
+//               window, for instance via a call to
+//               make_mono_display_region().  You are responsible for
+//               ensuring that the new DisplayRegion covers the entire
+//               window.  The previous overlay display region is not
+//               automatically removed; you must explicitly call
+//               remove_display_region() on it after replacing it with
+//               this method, if you wish it to be removed.
+//
+//               Normally, there is no reason to change the overlay
+//               DisplayRegion, so this method should be used only
+//               in very unusual circumstances.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+set_overlay_display_region(DisplayRegion *display_region) {
+  nassertv(display_region->get_window() == this);
+  _overlay_display_region = display_region;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::get_num_display_regions
 //       Access: Published
@@ -653,128 +797,6 @@ get_active_display_region(int n) const {
   return result;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOuput::create_texture_card_vdata
-//       Access: Private
-//  Description: Generates a GeomVertexData for a texture card.
-////////////////////////////////////////////////////////////////////
-PT(GeomVertexData) GraphicsOutput::
-create_texture_card_vdata(int x, int y) {
-  float xhi = 1.0;
-  float yhi = 1.0;
-
-  if (Texture::get_textures_power_2() != ATS_none) {
-    int xru = Texture::up_to_power_2(x);
-    int yru = Texture::up_to_power_2(y);
-    xhi = (x * 1.0f) / xru;
-    yhi = (y * 1.0f) / yru;
-  }
-
-  CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3n3t2();
-
-  PT(GeomVertexData) vdata = new GeomVertexData
-    ("card", format, Geom::UH_static);
-
-  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
-  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
-  GeomVertexWriter normal(vdata, InternalName::get_normal());
-
-  vertex.add_data3f(Vertexf::rfu(-1.0f, 0.0f,  1.0f));
-  vertex.add_data3f(Vertexf::rfu(-1.0f, 0.0f, -1.0f));
-  vertex.add_data3f(Vertexf::rfu( 1.0f, 0.0f,  1.0f));
-  vertex.add_data3f(Vertexf::rfu( 1.0f, 0.0f, -1.0f));
-
-  texcoord.add_data2f( 0.0f,  yhi);
-  texcoord.add_data2f( 0.0f, 0.0f);
-  texcoord.add_data2f(  xhi,  yhi);
-  texcoord.add_data2f(  xhi, 0.0f);
-
-  normal.add_data3f(LVector3f::back());
-  normal.add_data3f(LVector3f::back());
-  normal.add_data3f(LVector3f::back());
-  normal.add_data3f(LVector3f::back());
-
-  return vdata;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::set_size_and_recalc
-//       Access: Public
-//  Description: Changes the x_size and y_size, then recalculates
-//               structures that depend on size.  The recalculation
-//               currently includes:
-//               - compute_pixels on all the graphics regions.
-//               - updating the texture card, if one is present.
-////////////////////////////////////////////////////////////////////
-void GraphicsOutput::
-set_size_and_recalc(int x, int y) {
-  _x_size = x;
-  _y_size = y;
-  _has_size = true;
-
-  int fb_x_size = get_fb_x_size();
-  int fb_y_size = get_fb_y_size();
-
-  TotalDisplayRegions::iterator dri;
-  for (dri = _total_display_regions.begin();
-       dri != _total_display_regions.end();
-       ++dri) {
-    (*dri)->compute_pixels_all_stages(fb_x_size, fb_y_size);
-  }
-
-  if (_texture_card != 0) {
-    _texture_card->set_vertex_data(create_texture_card_vdata(x, y));
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::get_texture_card
-//       Access: Published
-//  Description: Returns a PandaNode containing a square polygon.
-//               The dimensions are (-1,0,-1) to (1,0,1). The texture
-//               coordinates are such that the texture of this
-//               GraphicsOutput is aligned properly to the polygon.
-//               The GraphicsOutput promises to surgically update
-//               the Geom inside the PandaNode if necessary to maintain
-//               this invariant.
-//
-//               Each invocation of this function returns a freshly-
-//               allocated PandaNode.  You can therefore safely modify
-//               the RenderAttribs of the PandaNode.  The
-//               PandaNode is initially textured with the texture
-//               of this GraphicOutput.
-////////////////////////////////////////////////////////////////////
-NodePath GraphicsOutput::
-get_texture_card() {
-  if (_texture_card == 0) {
-    PT(GeomVertexData) vdata = create_texture_card_vdata(_x_size, _y_size);
-    PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
-    strip->set_shade_model(Geom::SM_uniform);
-    strip->add_next_vertices(4);
-    strip->close_primitive();
-    _texture_card = new Geom(vdata);
-    _texture_card->add_primitive(strip);
-  }
-
-  PT(GeomNode) gnode = new GeomNode("texture card");
-  gnode->add_geom(_texture_card);
-  NodePath path(gnode);
-
-  // The texture card, by default, is textured with the first
-  // render-to-texture output texture.  Depth and stencil
-  // textures are ignored.  The user can freely alter the
-  // card's texture attrib.
-  for (int i=0; i<count_textures(); i++) {
-    Texture *texture = get_texture(i);
-    if ((texture->get_format() != Texture::F_depth_stencil)) {
-      path.set_texture(texture, 0);
-      break;
-    }
-  }
-
-  return path;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::make_texture_buffer
 //       Access: Published
@@ -929,6 +951,75 @@ make_cube_map(const string &name, int size, NodePath &camera_rig,
   return buffer;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_texture_card
+//       Access: Published
+//  Description: Returns a PandaNode containing a square polygon.
+//               The dimensions are (-1,0,-1) to (1,0,1). The texture
+//               coordinates are such that the texture of this
+//               GraphicsOutput is aligned properly to the polygon.
+//               The GraphicsOutput promises to surgically update
+//               the Geom inside the PandaNode if necessary to maintain
+//               this invariant.
+//
+//               Each invocation of this function returns a freshly-
+//               allocated PandaNode.  You can therefore safely modify
+//               the RenderAttribs of the PandaNode.  The
+//               PandaNode is initially textured with the texture
+//               of this GraphicOutput.
+////////////////////////////////////////////////////////////////////
+NodePath GraphicsOutput::
+get_texture_card() {
+  if (_texture_card == 0) {
+    PT(GeomVertexData) vdata = create_texture_card_vdata(_x_size, _y_size);
+    PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
+    strip->set_shade_model(Geom::SM_uniform);
+    strip->add_next_vertices(4);
+    strip->close_primitive();
+    _texture_card = new Geom(vdata);
+    _texture_card->add_primitive(strip);
+  }
+
+  PT(GeomNode) gnode = new GeomNode("texture card");
+  gnode->add_geom(_texture_card);
+  NodePath path(gnode);
+
+  // The texture card, by default, is textured with the first
+  // render-to-texture output texture.  Depth and stencil
+  // textures are ignored.  The user can freely alter the
+  // card's texture attrib.
+  for (int i=0; i<count_textures(); i++) {
+    Texture *texture = get_texture(i);
+    if ((texture->get_format() != Texture::F_depth_stencil)) {
+      path.set_texture(texture, 0);
+      break;
+    }
+  }
+
+  return path;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::share_depth_buffer
+//       Access: Published, Virtual
+//  Description: Will attempt to use the depth buffer of the input
+//               graphics_output. The buffer sizes must be exactly
+//               the same.
+////////////////////////////////////////////////////////////////////
+bool GraphicsOutput::
+share_depth_buffer(GraphicsOutput *graphics_output) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::unshare_depth_buffer
+//       Access: Published, Virtual
+//  Description: Discontinue sharing the depth buffer.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+unshare_depth_buffer() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::get_host
 //       Access: Public, Virtual
@@ -1004,71 +1095,33 @@ clear_pipe() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::begin_frame
-//       Access: Public, Virtual
-//  Description: This function will be called within the draw thread
-//               before beginning rendering for a given frame.  It
-//               should do whatever setup is required, and return true
-//               if the frame should be rendered, or false if it
-//               should be skipped.
-////////////////////////////////////////////////////////////////////
-bool GraphicsOutput::
-begin_frame(FrameMode mode, Thread *current_thread) {
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::end_frame
-//       Access: Public, Virtual
-//  Description: This function will be called within the draw thread
-//               after rendering is completed for a given frame.  It
-//               should do whatever finalization is required.
+//     Function: GraphicsOutput::set_size_and_recalc
+//       Access: Public
+//  Description: Changes the x_size and y_size, then recalculates
+//               structures that depend on size.  The recalculation
+//               currently includes:
+//               - compute_pixels on all the graphics regions.
+//               - updating the texture card, if one is present.
 ////////////////////////////////////////////////////////////////////
 void GraphicsOutput::
-end_frame(FrameMode mode, Thread *current_thread) {
-}
+set_size_and_recalc(int x, int y) {
+  _x_size = x;
+  _y_size = y;
+  _has_size = true;
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::pixel_factor_changed
-//       Access: Published, Virtual
-//  Description: Called internally when the pixel factor changes.
-////////////////////////////////////////////////////////////////////
-void GraphicsOutput::
-pixel_factor_changed() {
-  if (_has_size) {
-    set_size_and_recalc(_x_size, _y_size);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::prepare_for_deletion
-//       Access: Protected
-//  Description: Set the delete flag, and do the usual cleanup
-//               activities associated with that.
-////////////////////////////////////////////////////////////////////
-void GraphicsOutput::
-prepare_for_deletion() {
-
-  _active = false;
-  _delete_flag = true;
-
-  // We have to be sure to remove all of the display regions
-  // immediately, so that circular reference counts can be cleared
-  // up (each display region keeps a pointer to a CullResult,
-  // which can hold all sorts of pointers).
-  remove_all_display_regions();
+  int fb_x_size = get_fb_x_size();
+  int fb_y_size = get_fb_y_size();
 
-  // If we were rendering directly to texture, we can't delete the
-  // buffer until the texture is gone too.
-  for (int i=0; i<count_textures(); i++) {
-    if (get_rtm_mode(i) == RTM_bind_or_copy) {
-      _hold_textures.push_back(get_texture(i));
-    }
+  TotalDisplayRegions::iterator dri;
+  for (dri = _total_display_regions.begin();
+       dri != _total_display_regions.end();
+       ++dri) {
+    (*dri)->compute_pixels_all_stages(fb_x_size, fb_y_size);
   }
 
-  // We have to be sure to clear the _textures pointers, though, or
-  // we'll end up holding a reference to the textures forever.
-  clear_render_textures();
+  if (_texture_card != 0) {
+    _texture_card->set_vertex_data(create_texture_card_vdata(x, y));
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1092,82 +1145,35 @@ clear(Thread *current_thread) {
 
     nassertv(_gsg != (GraphicsStateGuardian *)NULL);
 
-    DisplayRegionPipelineReader dr_reader(_default_display_region, current_thread);
+    DisplayRegionPipelineReader dr_reader(_overlay_display_region, current_thread);
     _gsg->prepare_display_region(&dr_reader, Lens::SC_mono);
     _gsg->clear(this);
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::copy_to_textures
-//       Access: Protected
-//  Description: For all textures marked RTM_copy_texture,
-//               RTM_copy_ram, RTM_triggered_copy_texture, or
-//               RTM_triggered_copy_ram, do the necessary copies.
-//
-//               Returns true if all copies are successful, false
-//               otherwise.
+//     Function: GraphicsOutput::begin_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               before beginning rendering for a given frame.  It
+//               should do whatever setup is required, and return true
+//               if the frame should be rendered, or false if it
+//               should be skipped.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsOutput::
-copy_to_textures() {
-  bool okflag = true;
-  for (int i = 0; i < count_textures(); ++i) {
-    RenderTextureMode rtm_mode = get_rtm_mode(i);
-    if ((rtm_mode == RTM_none) || (rtm_mode == RTM_bind_or_copy)) {
-      continue;
-    }
-
-    Texture *texture = get_texture(i);
-    PStatTimer timer(_copy_texture_pcollector);
-    nassertr(has_texture(), false);
-
-    if ((rtm_mode == RTM_copy_texture)||
-        (rtm_mode == RTM_copy_ram)||
-        ((rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
-        ((rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
-      if (display_cat.is_debug()) {
-        display_cat.debug()
-          << "Copying texture for " << get_name() << " at frame end.\n";
-        display_cat.debug()
-          << "cube_map_index = " << _cube_map_index << "\n";
-      }
-      int plane = get_texture_plane(i);
-      RenderBuffer buffer(_gsg, DrawableRegion::get_renderbuffer_type(plane));
-      if (plane == RTP_color) {
-        buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
-                                         get_fb_properties());
-      }
-
-      bool copied = false;
-      if (_cube_map_dr != (DisplayRegion *)NULL) {
-        if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
-          copied =
-            _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
-                                          _cube_map_dr, buffer);
-        } else {
-          copied =
-            _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
-                                              _cube_map_dr, buffer);
-        }
-      } else {
-        if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
-          copied =
-            _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
-                                          _default_display_region, buffer);
-        } else {
-          copied =
-            _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
-                                              _default_display_region, buffer);
-        }
-      }
-      if (!copied) {
-        okflag = false;
-      }
-    }
-  }
-  _trigger_copy = false;
+begin_frame(FrameMode mode, Thread *current_thread) {
+  return false;
+}
 
-  return okflag;
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::end_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after rendering is completed for a given frame.  It
+//               should do whatever finalization is required.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+end_frame(FrameMode mode, Thread *current_thread) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1299,6 +1305,164 @@ void GraphicsOutput::
 process_events() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::pixel_factor_changed
+//       Access: Published, Virtual
+//  Description: Called internally when the pixel factor changes.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+pixel_factor_changed() {
+  if (_has_size) {
+    set_size_and_recalc(_x_size, _y_size);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::prepare_for_deletion
+//       Access: Protected
+//  Description: Set the delete flag, and do the usual cleanup
+//               activities associated with that.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+prepare_for_deletion() {
+  _active = false;
+  _delete_flag = true;
+
+  // We have to be sure to remove all of the display regions
+  // immediately, so that circular reference counts can be cleared
+  // up (each display region keeps a pointer to a CullResult,
+  // which can hold all sorts of pointers).
+  remove_all_display_regions();
+
+  // If we were rendering directly to texture, we can't delete the
+  // buffer until the texture is gone too.
+  for (int i=0; i<count_textures(); i++) {
+    if (get_rtm_mode(i) == RTM_bind_or_copy) {
+      _hold_textures.push_back(get_texture(i));
+    }
+  }
+
+  // We have to be sure to clear the _textures pointers, though, or
+  // we'll end up holding a reference to the textures forever.
+  clear_render_textures();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::copy_to_textures
+//       Access: Protected
+//  Description: For all textures marked RTM_copy_texture,
+//               RTM_copy_ram, RTM_triggered_copy_texture, or
+//               RTM_triggered_copy_ram, do the necessary copies.
+//
+//               Returns true if all copies are successful, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool GraphicsOutput::
+copy_to_textures() {
+  bool okflag = true;
+  for (int i = 0; i < count_textures(); ++i) {
+    RenderTextureMode rtm_mode = get_rtm_mode(i);
+    if ((rtm_mode == RTM_none) || (rtm_mode == RTM_bind_or_copy)) {
+      continue;
+    }
+
+    Texture *texture = get_texture(i);
+    PStatTimer timer(_copy_texture_pcollector);
+    nassertr(has_texture(), false);
+
+    if ((rtm_mode == RTM_copy_texture)||
+        (rtm_mode == RTM_copy_ram)||
+        ((rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
+        ((rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
+      if (display_cat.is_debug()) {
+        display_cat.debug()
+          << "Copying texture for " << get_name() << " at frame end.\n";
+        display_cat.debug()
+          << "cube_map_index = " << _cube_map_index << "\n";
+      }
+      int plane = get_texture_plane(i);
+      RenderBuffer buffer(_gsg, DrawableRegion::get_renderbuffer_type(plane));
+      if (plane == RTP_color) {
+        buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
+                                         get_fb_properties());
+      }
+
+      bool copied = false;
+      if (_cube_map_dr != (DisplayRegion *)NULL) {
+        if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
+          copied =
+            _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
+                                          _cube_map_dr, buffer);
+        } else {
+          copied =
+            _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
+                                              _cube_map_dr, buffer);
+        }
+      } else {
+        if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
+          copied =
+            _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
+                                          _overlay_display_region, buffer);
+        } else {
+          copied =
+            _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
+                                              _overlay_display_region, buffer);
+        }
+      }
+      if (!copied) {
+        okflag = false;
+      }
+    }
+  }
+  _trigger_copy = false;
+
+  return okflag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOuput::create_texture_card_vdata
+//       Access: Private
+//  Description: Generates a GeomVertexData for a texture card.
+////////////////////////////////////////////////////////////////////
+PT(GeomVertexData) GraphicsOutput::
+create_texture_card_vdata(int x, int y) {
+  float xhi = 1.0;
+  float yhi = 1.0;
+
+  if (Texture::get_textures_power_2() != ATS_none) {
+    int xru = Texture::up_to_power_2(x);
+    int yru = Texture::up_to_power_2(y);
+    xhi = (x * 1.0f) / xru;
+    yhi = (y * 1.0f) / yru;
+  }
+
+  CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3n3t2();
+
+  PT(GeomVertexData) vdata = new GeomVertexData
+    ("card", format, Geom::UH_static);
+
+  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
+  GeomVertexWriter normal(vdata, InternalName::get_normal());
+
+  vertex.add_data3f(Vertexf::rfu(-1.0f, 0.0f,  1.0f));
+  vertex.add_data3f(Vertexf::rfu(-1.0f, 0.0f, -1.0f));
+  vertex.add_data3f(Vertexf::rfu( 1.0f, 0.0f,  1.0f));
+  vertex.add_data3f(Vertexf::rfu( 1.0f, 0.0f, -1.0f));
+
+  texcoord.add_data2f( 0.0f,  yhi);
+  texcoord.add_data2f( 0.0f, 0.0f);
+  texcoord.add_data2f(  xhi,  yhi);
+  texcoord.add_data2f(  xhi, 0.0f);
+
+  normal.add_data3f(LVector3f::back());
+  normal.add_data3f(LVector3f::back());
+  normal.add_data3f(LVector3f::back());
+  normal.add_data3f(LVector3f::back());
+
+  return vdata;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::add_display_region
 //       Access: Private
@@ -1322,7 +1486,7 @@ add_display_region(DisplayRegion *display_region) {
 ////////////////////////////////////////////////////////////////////
 bool GraphicsOutput::
 do_remove_display_region(DisplayRegion *display_region) {
-  nassertr(display_region != _default_display_region, false);
+  nassertr(display_region != _overlay_display_region, false);
 
   PT(DisplayRegion) drp = display_region;
   TotalDisplayRegions::iterator dri =
@@ -1374,6 +1538,56 @@ do_determine_display_regions() {
   stable_sort(_active_display_regions.begin(), _active_display_regions.end(), IndirectLess<DisplayRegion>());
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::parse_color_mask
+//       Access: Private, Static
+//  Description: Parses one of the keywords in the
+//               red-blue-stereo-colors Config.prc variable, and
+//               returns the corresponding bitmask.
+//
+//               These bitmask values are taken from ColorWriteAttrib.
+////////////////////////////////////////////////////////////////////
+unsigned int GraphicsOutput::
+parse_color_mask(const string &word) {
+  unsigned int result = 0;
+  vector_string components;
+  tokenize(word, components, "|");
+
+  vector_string::const_iterator ci;
+  for (ci = components.begin(); ci != components.end(); ++ci) {
+    string w = downcase(*ci);
+    if (w == "red" || w == "r") {
+      result |= 0x001;
+
+    } else if (w == "green" || w == "g") {
+      result |= 0x002;
+
+    } else if (w == "blue" || w == "b") {
+      result |= 0x004;
+
+    } else if (w == "yellow" || w == "y") {
+      result |= 0x003;
+
+    } else if (w == "magenta" || w == "m") {
+      result |= 0x005;
+
+    } else if (w == "cyan" || w == "c") {
+      result |= 0x006;
+
+    } else if (w == "alpha" || w == "a") {
+      result |= 0x008;
+
+    } else if (w == "off") {
+      
+    } else {
+      display_cat.warning()
+        << "Invalid color in red-blue-stereo-colors: " << (*ci) << "\n";
+    }
+  }
+
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::FrameMode output operator
 //  Description:
@@ -1391,25 +1605,3 @@ operator << (ostream &out, GraphicsOutput::FrameMode fm) {
 
   return out << "(**invalid GraphicsOutput::FrameMode(" << (int)fm << ")**)";
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::share_depth_buffer
-//       Access: Published, Virtual
-//  Description: Will attempt to use the depth buffer of the input
-//               graphics_output. The buffer sizes must be exactly
-//               the same.
-////////////////////////////////////////////////////////////////////
-bool GraphicsOutput::
-share_depth_buffer(GraphicsOutput *graphics_output) {
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::unshare_depth_buffer
-//       Access: Published, Virtual
-//  Description: Discontinue sharing the depth buffer.
-////////////////////////////////////////////////////////////////////
-void GraphicsOutput::
-unshare_depth_buffer() {
-
-}

+ 41 - 17
panda/src/display/graphicsOutput.h

@@ -24,7 +24,7 @@
 #include "drawableRegion.h"
 #include "renderBuffer.h"
 #include "graphicsOutputBase.h"
-
+#include "luse.h"
 #include "typedWritableReferenceCount.h"
 #include "pandaNode.h"
 #include "pStatCollector.h"
@@ -67,7 +67,8 @@ protected:
                  const FrameBufferProperties &fb_prop,
                  const WindowProperties &win_prop, int flags,
                  GraphicsStateGuardian *gsg,
-                 GraphicsOutput *host);
+                 GraphicsOutput *host,
+                 bool default_stereo_flags);
 
 private:
   GraphicsOutput(const GraphicsOutput &copy);
@@ -111,6 +112,10 @@ PUBLISHED:
   INLINE int get_y_size() const;
   INLINE int get_fb_x_size() const;
   INLINE int get_fb_y_size() const;
+  INLINE int get_sbs_left_x_size() const;
+  INLINE int get_sbs_left_y_size() const;
+  INLINE int get_sbs_right_x_size() const;
+  INLINE int get_sbs_right_y_size() const;
   INLINE bool has_size() const;
   INLINE bool is_valid() const;
 
@@ -130,6 +135,14 @@ PUBLISHED:
   INLINE unsigned int get_left_eye_color_mask() const;
   INLINE unsigned int get_right_eye_color_mask() const;
 
+  void set_side_by_side_stereo(bool side_by_side_stereo);
+  void set_side_by_side_stereo(bool side_by_side_stereo,
+                               const LVecBase4f &sbs_left_dimensions,
+                               const LVecBase4f &sbs_right_dimensions);
+  INLINE bool get_side_by_side_stereo() const;
+  INLINE const LVecBase4f &get_sbs_left_dimensions() const;
+  INLINE const LVecBase4f &get_sbs_right_dimensions() const;
+
   INLINE const FrameBufferProperties &get_fb_properties() const;
   INLINE bool is_stereo() const;
 
@@ -146,14 +159,20 @@ PUBLISHED:
   INLINE void trigger_copy();
   
   INLINE DisplayRegion *make_display_region();
-  DisplayRegion *make_display_region(float l, float r, float b, float t);
+  INLINE DisplayRegion *make_display_region(float l, float r, float b, float t);
+  DisplayRegion *make_display_region(const LVecBase4f &dimensions);
   INLINE DisplayRegion *make_mono_display_region();
-  DisplayRegion *make_mono_display_region(float l, float r, float b, float t);
+  INLINE DisplayRegion *make_mono_display_region(float l, float r, float b, float t);
+  DisplayRegion *make_mono_display_region(const LVecBase4f &dimensions);
   INLINE StereoDisplayRegion *make_stereo_display_region();
-  StereoDisplayRegion *make_stereo_display_region(float l, float r, float b, float t);
+  INLINE StereoDisplayRegion *make_stereo_display_region(float l, float r, float b, float t);
+  StereoDisplayRegion *make_stereo_display_region(const LVecBase4f &dimensions);
   bool remove_display_region(DisplayRegion *display_region);
   void remove_all_display_regions();
 
+  INLINE DisplayRegion *get_overlay_display_region() const;
+  void set_overlay_display_region(DisplayRegion *display_region);
+
   int get_num_display_regions() const;
   PT(DisplayRegion) get_display_region(int n) const;
   MAKE_SEQ(get_display_regions, get_num_display_regions, get_display_region);
@@ -233,8 +252,20 @@ protected:
   INLINE void clear_cube_map_selection();
   INLINE void trigger_flip();
 
-protected:
+private:
+  PT(GeomVertexData) create_texture_card_vdata(int x, int y);
+  
+  DisplayRegion *add_display_region(DisplayRegion *display_region);
+  bool do_remove_display_region(DisplayRegion *display_region);
+
+  INLINE void win_display_regions_changed();
+
+  INLINE void determine_display_regions() const;
+  void do_determine_display_regions();
+
+  static unsigned int parse_color_mask(const string &word);
 
+protected:
   class RenderTexture {
   public:
     PT(Texture) _texture;
@@ -256,16 +287,6 @@ protected:
   bool _trigger_copy;
   
 private:
-  PT(GeomVertexData) create_texture_card_vdata(int x, int y);
-  
-  DisplayRegion *add_display_region(DisplayRegion *display_region);
-  bool do_remove_display_region(DisplayRegion *display_region);
-
-  INLINE void win_display_regions_changed();
-
-  INLINE void determine_display_regions() const;
-  void do_determine_display_regions();
-  
   int _sort;
   int _child_sort;
   bool _got_child_sort;
@@ -278,6 +299,9 @@ protected:
   bool _red_blue_stereo;
   unsigned int _left_eye_color_mask;
   unsigned int _right_eye_color_mask;
+  bool _side_by_side_stereo;
+  LVecBase4f _sbs_left_dimensions;
+  LVecBase4f _sbs_right_dimensions;
   bool _delete_flag;
 
   // These weak pointers are used to keep track of whether the
@@ -289,7 +313,7 @@ protected:
 protected:
   LightMutex _lock; 
   // protects _display_regions.
-  PT(DisplayRegion) _default_display_region;
+  PT(DisplayRegion) _overlay_display_region;
   typedef pvector< PT(DisplayRegion) > TotalDisplayRegions;
   TotalDisplayRegions _total_display_regions;
   typedef pvector<DisplayRegion *> ActiveDisplayRegions;

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

@@ -1273,8 +1273,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, LMatrix4f
 //  Description: Return a pointer to struct ShaderPtrData
 ////////////////////////////////////////////////////////////////////
 const Shader::ShaderPtrData *GraphicsStateGuardian::
-  fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec){
-    return (_target_shader->get_shader_input_ptr(spec._arg));
+fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec) {
+  return (_target_shader->get_shader_input_ptr(spec._arg));
 }
 
 ////////////////////////////////////////////////////////////////////

+ 0 - 3
panda/src/display/graphicsWindow.h

@@ -140,9 +140,6 @@ protected:
   void system_changed_properties(const WindowProperties &properties);
   void system_changed_size(int x_size, int y_size);
 
-private:
-  static unsigned int parse_color_mask(const string &word);
-
 protected:
   INLINE void add_input_device(const GraphicsWindowInputDevice &device);
   typedef vector_GraphicsWindowInputDevice InputDevices;

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

@@ -30,7 +30,7 @@ ParasiteBuffer(GraphicsOutput *host, const string &name,
   GraphicsOutput(host->get_engine(), host->get_pipe(), 
                  name, host->get_fb_properties(),
                  WindowProperties::size(x_size, y_size), flags, 
-                 host->get_gsg(), host)
+                 host->get_gsg(), host, false)
 {
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, this);
@@ -52,7 +52,7 @@ ParasiteBuffer(GraphicsOutput *host, const string &name,
   _x_size = x_size;
   _y_size = y_size;
   _has_size = true;
-  _default_display_region->compute_pixels(_x_size, _y_size);
+  _overlay_display_region->compute_pixels(_x_size, _y_size);
   _is_valid = true;
   
   set_inverted(host->get_gsg()->get_copy_texture_inverted());

+ 6 - 6
panda/src/display/stereoDisplayRegion.cxx

@@ -24,9 +24,9 @@ TypeHandle StereoDisplayRegion::_type_handle;
 ////////////////////////////////////////////////////////////////////
 StereoDisplayRegion::
 StereoDisplayRegion(GraphicsOutput *window,
-                    float l, float r, float b, float t,
+                    const LVecBase4f &dimensions,
                     DisplayRegion *left, DisplayRegion *right) :
-  DisplayRegion(window, l, r, b, t),
+  DisplayRegion(window, dimensions),
   _left_eye(left),
   _right_eye(right)
 {
@@ -115,10 +115,10 @@ set_pixel_zoom(float pixel_zoom) {
 //               indicated dimensions.
 ////////////////////////////////////////////////////////////////////
 void StereoDisplayRegion::
-set_dimensions(float l, float r, float b, float t) {
-  DisplayRegion::set_dimensions(l, r, b, t);
-  _left_eye->set_dimensions(l, r, b, t);
-  _right_eye->set_dimensions(l, r, b, t);
+set_dimensions(const LVecBase4f &dimensions) {
+  DisplayRegion::set_dimensions(dimensions);
+  _left_eye->set_dimensions(dimensions);
+  _right_eye->set_dimensions(dimensions);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/display/stereoDisplayRegion.h

@@ -37,7 +37,7 @@
 class EXPCL_PANDA_DISPLAY StereoDisplayRegion : public DisplayRegion {
 protected:
   StereoDisplayRegion(GraphicsOutput *window,
-                      float l, float r, float b, float t,
+                      const LVecBase4f &dimensions,
                       DisplayRegion *left, DisplayRegion *right);
 
 public:
@@ -51,7 +51,7 @@ PUBLISHED:
   virtual void set_pixel_zoom(float pixel_zoom);
 
   // Inherited from DisplayRegion
-  virtual void set_dimensions(float l, float r, float b, float t);
+  virtual void set_dimensions(const LVecBase4f &dimensions);
   virtual bool is_stereo() const;
   virtual void set_camera(const NodePath &camera);
   virtual void set_active(bool active);

+ 1 - 1
panda/src/display/subprocessWindow.cxx

@@ -192,7 +192,7 @@ begin_flip() {
 
   bool copied = 
     _gsg->framebuffer_copy_to_ram(_texture, -1,
-                                  _default_display_region, buffer);
+                                  _overlay_display_region, buffer);
 
   if (copied) {
     CPTA_uchar image = _texture->get_ram_image();

+ 16 - 7
panda/src/framework/windowFramework.cxx

@@ -336,8 +336,8 @@ get_aspect_2d() {
       this_aspect_ratio = 1.0f;
 
       if (_window->has_size()) {
-        int x_size = _window->get_x_size();
-        int y_size = _window->get_y_size();
+        int x_size = _window->get_sbs_left_x_size();
+        int y_size = _window->get_sbs_left_y_size();
         if (y_size != 0) {
           this_aspect_ratio = (float)x_size / (float)y_size;
         }
@@ -370,7 +370,16 @@ get_mouse() {
     // Another advantage to using a MouseWatcher is that the PGTop of
     // aspect2d likes it better.
     PT(MouseWatcher) mw = new MouseWatcher("watcher");
-    mw->set_display_region(_display_region_3d);
+
+    if (_window->get_side_by_side_stereo()) {
+      // If the window has side-by-side stereo enabled, then
+      // we should constrain the MouseWatcher to the window's
+      // DisplayRegion.  This will enable the MouseWatcher to
+      // track the left and right halves of the screen
+      // individually.
+      mw->set_display_region(_window->get_overlay_display_region());
+    }
+
     _mouse = mouse.attach_new_node(mw);
   }
   return _mouse;
@@ -806,8 +815,8 @@ adjust_aspect_ratio() {
     this_aspect_ratio = 1.0f;
     
     if (_window->has_size()) {
-      x_size = _window->get_x_size();
-      y_size = _window->get_y_size();
+      x_size = _window->get_sbs_left_x_size();
+      y_size = _window->get_sbs_left_y_size();
       if (y_size != 0) {
         this_aspect_ratio = (float)x_size / (float)y_size;
       }
@@ -1151,8 +1160,8 @@ make_camera() {
     // Otherwise, infer the aspect ratio from the window size.  This
     // does assume we have square pixels on our output device.
     if (_window->has_size()) {
-      int x_size = _window->get_x_size();
-      int y_size = _window->get_y_size();
+      int x_size = _window->get_sbs_left_x_size();
+      int y_size = _window->get_sbs_left_y_size();
       if (y_size != 0) {
         lens->set_film_size(x_size, y_size);
       }

+ 34 - 16
panda/src/mathutil/triangulator.cxx

@@ -151,20 +151,36 @@ triangulate() {
   */
   choose_idx = 0;
 
-  //  cerr << "got " << num_segments << " segments\n";
-  /*
+  cerr << "got " << num_segments << " segments\n";
   for (i = 1; i < (int)seg.size(); ++i) {
     segment_t &s = seg[i];
     printf("  %d. (%g %g), (%g %g)\n", i, s.v0.x, s.v0.y, s.v1.x, s.v1.y);
     printf("    root0 = %d, root1 = %d\n", s.root0, s.root1);
     printf("    next = %d, prev = %d\n", s.next, s.prev);
   }
-  */
 
-  construct_trapezoids(num_segments);
+  while (construct_trapezoids(num_segments) != 0) {
+    // If there's an error, re-shuffle the index and try again.
+    Randomizer randomizer;
+    for (i = 0; i < num_segments; ++i) {
+      int j = randomizer.random_int(num_segments);
+      nassertv(j >= 0 && j < num_segments);
+      int t = permute[i];
+      permute[i] = permute[j];
+      permute[j] = t;
+    }
+    choose_idx = 0;
+
+    cerr << "got " << num_segments << " segments\n";
+    for (i = 1; i < (int)seg.size(); ++i) {
+      segment_t &s = seg[i];
+      printf("  %d. (%g %g), (%g %g)\n", i, s.v0.x, s.v0.y, s.v1.x, s.v1.y);
+      printf("    root0 = %d, root1 = %d\n", s.root0, s.root1);
+      printf("    next = %d, prev = %d\n", s.next, s.prev);
+    }
+  }
 
-  //  cerr << "got " << tr.size() - 1 << " trapezoids\n";
-  /*
+  cerr << "got " << tr.size() - 1 << " trapezoids\n";
   for (i = 1; i < (int)tr.size(); ++i) {
     trap_t &t = tr[i];
     cerr << "  " << i << ". state = " << t.state << "\n";
@@ -173,21 +189,17 @@ triangulate() {
     cerr << "    hi = " << t.hi.x << " " << t.hi.y << " lo = "
          << t.lo.x << " " << t.lo.y << "\n";
   }
-  */
 
   int nmonpoly = monotonate_trapezoids(num_segments);
 
-  //  cerr << "got " << nmonpoly << " monotone polygons\n";
+  cerr << "got " << nmonpoly << " monotone polygons\n";
 
   triangulate_monotone_polygons(num_segments, nmonpoly);
-
-  /*
   Result::iterator ri;
   for (ri = _result.begin(); ri != _result.end(); ++ri) {
     cerr << "tri: " << (*ri)._v0 << " " << (*ri)._v1 << " "
          << (*ri)._v2 << "\n";
   }
-  */
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -822,7 +834,7 @@ merge_trapezoids(int segnum, int tfirst, int tlast, int side) {
 
 int Triangulator::
 add_segment(int segnum) {
-  //  cerr << "add_segment(" << segnum << ")\n";
+  cerr << "add_segment(" << segnum << ")\n";
 
   segment_t s;
   //  segment_t *so = &seg[segnum];
@@ -1011,8 +1023,9 @@ add_segment(int segnum) {
 
       if ((tr[t].d0 <= 0) && (tr[t].d1 <= 0)) /* case cannot arise */
         {
+          /* Actually, this case does sometimes arise.  Huh. */
           fprintf(stderr, "add_segment: error\n");
-          break;
+          return 1;
         }
       
       /* only one trapezoid below. partition t into two and make the */
@@ -1445,7 +1458,7 @@ find_new_roots(int segnum) {
 /* Main routine to perform trapezoidation */
 int Triangulator::
 construct_trapezoids(int nseg) {
-  //  cerr << "construct_trapezoids(" << nseg << ")\n";
+  cerr << "construct_trapezoids(" << nseg << ")\n";
   int i;
   int root, h;
   
@@ -1462,7 +1475,10 @@ construct_trapezoids(int nseg) {
   for (h = 1; h <= math_logstar_n(nseg); h++)
     {
       for (i = math_N(nseg, h -1) + 1; i <= math_N(nseg, h); i++) {
-        add_segment(choose_segment());
+        if (add_segment(choose_segment()) != 0) {
+          // error in add_segment.
+          return 1;
+        }
       }
       
       /* Find a new root for each of the segment endpoints */
@@ -2129,7 +2145,7 @@ triangulate_single_polygon(int nvert, int posmax, int side) {
   int ri;
   int endv, tmp, vpos;
 
-  //  cerr << "triangulate_single_polygon(" << nvert << ", " << posmax << ", " << side << ")\n";
+  cerr << "triangulate_single_polygon(" << nvert << ", " << posmax << ", " << side << ")\n";
 
   if (side == TRI_RHS)          /* RHS segment is a single segment */
     {
@@ -2160,6 +2176,8 @@ triangulate_single_polygon(int nvert, int posmax, int side) {
   
   while ((v != endv) || (ri > 1))
     {
+      cerr << " v = " << v << " ri = " << ri << " rc = " << rc.size()
+           << " _result = " << _result.size() << "\n";
       if (v <= 0) {
         // Something went wrong.
         return;

+ 63 - 31
panda/src/tform/mouseWatcher.cxx

@@ -25,6 +25,7 @@
 #include "dataNodeTransmit.h"
 #include "transformState.h"
 #include "displayRegion.h"
+#include "stereoDisplayRegion.h"
 #include "geomVertexWriter.h"
 #include "geomLinestrips.h"
 #include "geomPoints.h"
@@ -1415,8 +1416,8 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
     DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr());
     DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr());
 
-    const LVecBase2f &f = xy->get_value();
-    const LVecBase2f &p = pixel_xy->get_value();
+    LVecBase2f f = xy->get_value();
+    LVecBase2f p = pixel_xy->get_value();
 
     // Asad: determine if mouse moved from last position
     const LVecBase2f &last_f = _xy->get_value();
@@ -1427,41 +1428,16 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
 
     if (_display_region != (DisplayRegion *)NULL) {
       // If we've got a display region, constrain the mouse to it.
-      DisplayRegionPipelineReader dr_reader(_display_region, current_thread);
-      float left, right, bottom, top;
-      dr_reader.get_dimensions(left, right, bottom, top);
+      if (constrain_display_region(_display_region, f, p, current_thread)) {
+        set_mouse(f, p);
 
-      // Need to translate this into DisplayRegion [0, 1] space
-      float x = (f[0] + 1.0f) / 2.0f;
-      float y = (f[1] + 1.0f) / 2.0f;
-      
-      if (x < left || x >= right || 
-          y < bottom || y >= top) {
+      } else {
         // The mouse is outside the display region, even though it's
         // within the window.  This is considered not having a mouse.
         set_no_mouse();
-
+        
         // This also means we should suppress mouse button events below us.
         _internal_suppress |= MouseWatcherRegion::SF_mouse_button;
-
-      } else {
-        // The mouse is within the display region; rescale it.
-
-        // Scale in DR space
-        float xp = (x - left) / (right - left);
-        // Translate back into [-1, 1] space
-        float xpp = (xp * 2.0f) - 1.0f;
-
-        float yp = (y - bottom) / (top - bottom);
-        float ypp = (yp * 2.0f) - 1.0f;
-
-        int xo, yo, w, h;
-        dr_reader.get_region_pixels_i(xo, yo, w, h);
-
-        LVecBase2f new_f(xpp, ypp);
-        LVecBase2f new_p(p[0] - xo, p[1] - yo);
-
-        set_mouse(new_f, new_p);
       }
 
     } else {
@@ -1678,3 +1654,59 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
     output.set_data(_button_events_output, EventParameter(_button_events));
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcher::constrain_display_region
+//       Access: Private, Static
+//  Description: Constrains the mouse coordinates to within the
+//               indicated DisplayRegion.  If the mouse pointer does
+//               indeed fall within the DisplayRegion, rescales f and
+//               p correspondingly, and returns true.  If the mouse
+//               pointer does not fall within the DisplayRegion,
+//               leaves f and p unchanged, and returns false.
+////////////////////////////////////////////////////////////////////
+bool MouseWatcher::
+constrain_display_region(DisplayRegion *display_region, 
+                         LVecBase2f &f, LVecBase2f &p,
+                         Thread *current_thread) {
+  // If it's a stereo DisplayRegion, we should actually call this
+  // method twice, once for each eye, in case we have side-by-side
+  // stereo.
+  if (display_region->is_stereo()) {
+    StereoDisplayRegion *stereo_display_region;
+    DCAST_INTO_R(stereo_display_region, display_region, false);
+    return constrain_display_region(stereo_display_region->get_left_eye(), f, p, current_thread) ||
+      constrain_display_region(stereo_display_region->get_right_eye(), f, p, current_thread);
+  }
+
+  DisplayRegionPipelineReader dr_reader(display_region, current_thread);
+  float left, right, bottom, top;
+  dr_reader.get_dimensions(left, right, bottom, top);
+  
+  // Need to translate this into DisplayRegion [0, 1] space
+  float x = (f[0] + 1.0f) / 2.0f;
+  float y = (f[1] + 1.0f) / 2.0f;
+  
+  if (x < left || x >= right || 
+      y < bottom || y >= top) {
+    // The mouse is outside the display region.
+    return false;
+  }
+    
+  // The mouse is within the display region; rescale it.
+  
+  // Scale in DR space
+  float xp = (x - left) / (right - left);
+  // Translate back into [-1, 1] space
+  float xpp = (xp * 2.0f) - 1.0f;
+  
+  float yp = (y - bottom) / (top - bottom);
+  float ypp = (yp * 2.0f) - 1.0f;
+  
+  int xo, yo, w, h;
+  dr_reader.get_region_pixels_i(xo, yo, w, h);
+  
+  f.set(xpp, ypp);
+  p.set(p[0] - xo, p[1] - yo);
+  return true;
+}

+ 4 - 0
panda/src/tform/mouseWatcher.h

@@ -206,6 +206,10 @@ private:
   void consider_keyboard_suppress(const MouseWatcherRegion *region);
   void discard_excess_trail_log();
   void update_trail_node();
+
+  static bool constrain_display_region(DisplayRegion *display_region, 
+                                       LVecBase2f &f, LVecBase2f &p,
+                                       Thread *current_thread);
   
 private:
   // This wants to be a set, but because you cannot export sets across