From 9124513b820a647c5394fffefb51cf1c88ce26b2 Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 19 Jul 2010 18:43:24 +0000 Subject: [PATCH] side-by-side stereo --- direct/src/showbase/ShowBase.py | 27 +- panda/src/display/config_display.cxx | 20 + panda/src/display/config_display.h | 3 + panda/src/display/displayRegion.I | 71 +- panda/src/display/displayRegion.cxx | 41 +- panda/src/display/displayRegion.h | 14 +- panda/src/display/graphicsBuffer.cxx | 4 +- panda/src/display/graphicsOutput.I | 185 ++++- panda/src/display/graphicsOutput.cxx | 770 ++++++++++++-------- panda/src/display/graphicsOutput.h | 58 +- panda/src/display/graphicsStateGuardian.cxx | 4 +- panda/src/display/graphicsWindow.h | 3 - panda/src/display/parasiteBuffer.cxx | 4 +- panda/src/display/stereoDisplayRegion.cxx | 12 +- panda/src/display/stereoDisplayRegion.h | 4 +- panda/src/display/subprocessWindow.cxx | 2 +- panda/src/framework/windowFramework.cxx | 23 +- panda/src/mathutil/triangulator.cxx | 50 +- panda/src/tform/mouseWatcher.cxx | 94 ++- panda/src/tform/mouseWatcher.h | 4 + 20 files changed, 956 insertions(+), 437 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 64784994e4..ba7d02f978 100644 --- a/direct/src/showbase/ShowBase.py +++ b/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. diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index caf897d445..d373ecb569 100644 --- a/panda/src/display/config_display.cxx +++ b/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 " diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index 702de29fa6..48edde4f19 100644 --- a/panda/src/display/config_display.h +++ b/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; diff --git a/panda/src/display/displayRegion.I b/panda/src/display/displayRegion.I index f74d0fdb53..01f19ffaf2 100644 --- a/panda/src/display/displayRegion.I +++ b/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]; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index e37a813b2e..cfe048f56d 100644 --- a/panda/src/display/displayRegion.cxx +++ b/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 ©) : - _l(copy._l), - _r(copy._r), - _b(copy._b), - _t(copy._t), + _dimensions(copy._dimensions), _pl(copy._pl), _pr(copy._pr), _pb(copy._pb), diff --git a/panda/src/display/displayRegion.h b/panda/src/display/displayRegion.h index c7fc55a9c6..cbdb6972b2 100644 --- a/panda/src/display/displayRegion.h +++ b/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 ©); @@ -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; diff --git a/panda/src/display/graphicsBuffer.cxx b/panda/src/display/graphicsBuffer.cxx index ae643593cc..702bc0509f 100644 --- a/panda/src/display/graphicsBuffer.cxx +++ b/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; } diff --git a/panda/src/display/graphicsOutput.I b/panda/src/display/graphicsOutput.I index 4e37ef4423..e230bb5fdf 100644 --- a/panda/src/display/graphicsOutput.I +++ b/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); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 5b83e690fc..92e418a7a9 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/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; - PT(StereoDisplayRegion) stereo = new StereoDisplayRegion(this, l, r, b, t, + 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, 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; iget_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; iget_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); - } -} + int fb_x_size = get_fb_x_size(); + int fb_y_size = get_fb_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; icompute_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; - } +begin_frame(FrameMode mode, Thread *current_thread) { + return false; +} - 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; - - 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; iget_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()); } +//////////////////////////////////////////////////////////////////// +// 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() { - -} diff --git a/panda/src/display/graphicsOutput.h b/panda/src/display/graphicsOutput.h index b69d584a58..d4fcf95dd9 100644 --- a/panda/src/display/graphicsOutput.h +++ b/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 ©); @@ -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 ActiveDisplayRegions; diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 8c24244bbc..c16f37d508 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/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)); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index a8873bf8f1..6a1f747591 100644 --- a/panda/src/display/graphicsWindow.h +++ b/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; diff --git a/panda/src/display/parasiteBuffer.cxx b/panda/src/display/parasiteBuffer.cxx index dfd1bac9e1..687f247071 100644 --- a/panda/src/display/parasiteBuffer.cxx +++ b/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()); diff --git a/panda/src/display/stereoDisplayRegion.cxx b/panda/src/display/stereoDisplayRegion.cxx index aacd367aa2..1f6ca9c36f 100644 --- a/panda/src/display/stereoDisplayRegion.cxx +++ b/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); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/stereoDisplayRegion.h b/panda/src/display/stereoDisplayRegion.h index 4967ecf4e4..6dca14fddf 100644 --- a/panda/src/display/stereoDisplayRegion.h +++ b/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); diff --git a/panda/src/display/subprocessWindow.cxx b/panda/src/display/subprocessWindow.cxx index 866cb621cf..a6daa99e97 100644 --- a/panda/src/display/subprocessWindow.cxx +++ b/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(); diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx index 6d4233c2c9..a234358161 100644 --- a/panda/src/framework/windowFramework.cxx +++ b/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); } diff --git a/panda/src/mathutil/triangulator.cxx b/panda/src/mathutil/triangulator.cxx index ead4324e9c..cc8a9ed796 100644 --- a/panda/src/mathutil/triangulator.cxx +++ b/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 " << tr.size() - 1 << " trapezoids\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); + } + } + + 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; diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index 42dd8af78a..ee2604446a 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/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; +} diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index 4bf5366824..0bbe28b3cc 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/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