From ffb92db0224c74caa41342f349b0db4001470dcf Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 16 Feb 2006 02:28:20 +0000 Subject: [PATCH] stereo rendering --- direct/src/showbase/ShowBase.py | 6 +- panda/src/display/displayRegion.I | 14 + panda/src/display/displayRegion.cxx | 83 ++++- panda/src/display/displayRegion.h | 11 +- panda/src/display/drawableRegion.I | 25 -- panda/src/display/drawableRegion.cxx | 35 ++ panda/src/display/drawableRegion.h | 5 +- panda/src/display/graphicsStateGuardian.I | 2 +- panda/src/display/graphicsStateGuardian.cxx | 2 +- panda/src/display/graphicsStateGuardian.h | 4 +- panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx | 7 +- panda/src/dxgsg8/dxGraphicsStateGuardian8.h | 2 +- panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx | 7 +- panda/src/dxgsg9/dxGraphicsStateGuardian9.h | 2 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 30 +- .../src/glstuff/glGraphicsStateGuardian_src.h | 4 +- panda/src/gobj/lens.cxx | 322 +++++++++++------- panda/src/gobj/lens.h | 67 ++-- panda/src/gobj/orthographicLens.cxx | 6 +- panda/src/gobj/perspectiveLens.cxx | 28 +- 20 files changed, 451 insertions(+), 211 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 20a0df1adb..74f5eb497f 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -746,7 +746,8 @@ class ShowBase(DirectObject.DirectObject): return aspectRatio def makeCamera(self, win, sort = 0, scene = None, - displayRegion = (0, 1, 0, 1), aspectRatio = None, + displayRegion = (0, 1, 0, 1), stereoChannel = None, + aspectRatio = None, lens = None, camName = 'cam'): """ Makes a new 3-d camera associated with the indicated window, @@ -755,6 +756,9 @@ class ShowBase(DirectObject.DirectObject): dr = win.makeDisplayRegion(*displayRegion) dr.setSort(sort) + if stereoChannel != None: + dr.setStereoChannel(stereoChannel) + if scene == None: scene = self.render diff --git a/panda/src/display/displayRegion.I b/panda/src/display/displayRegion.I index 68a0bdddc0..cd7931fcea 100644 --- a/panda/src/display/displayRegion.I +++ b/panda/src/display/displayRegion.I @@ -52,6 +52,20 @@ get_sort() const { return cdata->_sort; } +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_stereo_channel +// Access: Published +// Description: Returns whether the DisplayRegion is specified as the +// left or right channel of a stereo pair, or whether it +// is a normal, monocular image. See +// set_stereo_channel(). +//////////////////////////////////////////////////////////////////// +INLINE Lens::StereoChannel DisplayRegion:: +get_stereo_channel() { + CDReader cdata(_cycler); + return cdata->_stereo_channel; +} + //////////////////////////////////////////////////////////////////// // Function: DisplayRegion::set_cube_map_index // Access: Published diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index 8770c3dc8a..ad6eec08df 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -75,7 +75,7 @@ operator = (const DisplayRegion&) { //////////////////////////////////////////////////////////////////// // Function: DisplayRegion::Destructor -// Access: Public +// Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// DisplayRegion:: @@ -305,8 +305,7 @@ set_active(bool active) { //////////////////////////////////////////////////////////////////// void DisplayRegion:: set_sort(int sort) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); + nassertv(Thread::get_current_pipeline_stage() == 0); CDReader cdata(_cycler); if (sort != cdata->_sort) { @@ -316,6 +315,53 @@ set_sort(int sort) { } } +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::set_stereo_channel +// Access: Published +// Description: Specifies whether the DisplayRegion represents the +// left or right channel of a stereo pair, or whether it +// is a normal, monocular image. See +// set_stereo_channel(). +// +// This controls which direction--to the left or the +// right--the view from a PerspectiveLens is shifted +// when it is used to render into this DisplayRegion. +// Also see Lens::set_interocular_distance() and +// Lens::set_convergence_distance(). +// +// Normally you would create at least two DisplayRegions +// for a stereo window, one for each of the left and +// right channels. The two DisplayRegions may share the +// same camera (and thus the same lens); this parameter +// is used to control the exact properties of the lens +// when it is used to render into this DisplayRegion. +// +// When the DisplayRegion is attached to a stereo window +// (one in which FrameBufferProperties::FM_stereo is +// set), this also specifies which physical channel the +// DisplayRegion renders to. +//////////////////////////////////////////////////////////////////// +void DisplayRegion:: +set_stereo_channel(Lens::StereoChannel stereo_channel) { + nassertv(Thread::get_current_pipeline_stage() == 0); + + CDWriter cdata(_cycler); + cdata->_stereo_channel = stereo_channel; + switch (stereo_channel) { + case Lens::SC_left: + cdata->_draw_buffer_mask = ~(RenderBuffer::T_front_right | RenderBuffer::T_back_right); + break; + + case Lens::SC_right: + cdata->_draw_buffer_mask = ~(RenderBuffer::T_front_left | RenderBuffer::T_back_left); + break; + + case Lens::SC_both: + cdata->_draw_buffer_mask = ~0; + break; + } +} + //////////////////////////////////////////////////////////////////// // Function: DisplayRegion::compute_pixels // Access: Published @@ -624,6 +670,33 @@ get_screenshot(PNMImage &image) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_screenshot_buffer_type +// Access: Public, Virtual +// Description: Returns the RenderBuffer that should be used for +// capturing screenshots from this particular +// DrawableRegion. +//////////////////////////////////////////////////////////////////// +int DisplayRegion:: +get_screenshot_buffer_type() const { + CDReader cdata(_cycler); + return _screenshot_buffer_type & cdata->_draw_buffer_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_draw_buffer_type +// Access: Public, Virtual +// Description: Returns the RenderBuffer into which the GSG should +// issue draw commands. Normally, this is the back +// buffer for double-buffered windows, and the front +// buffer for single-buffered windows. +//////////////////////////////////////////////////////////////////// +int DisplayRegion:: +get_draw_buffer_type() const { + CDReader cdata(_cycler); + return _draw_buffer_type & cdata->_draw_buffer_mask; +} + //////////////////////////////////////////////////////////////////// // Function: DisplayRegion::win_display_regions_changed // Access: Private @@ -686,6 +759,8 @@ CData() : _camera_node((Camera *)NULL), _active(true), _sort(0), + _stereo_channel(Lens::SC_both), + _draw_buffer_mask(~0), _cube_map_index(-1) { } @@ -711,6 +786,8 @@ CData(const DisplayRegion::CData ©) : _camera_node(copy._camera_node), _active(copy._active), _sort(copy._sort), + _stereo_channel(copy._stereo_channel), + _draw_buffer_mask(copy._draw_buffer_mask), _cube_map_index(copy._cube_map_index) { } diff --git a/panda/src/display/displayRegion.h b/panda/src/display/displayRegion.h index 2848858b9e..84f4562242 100644 --- a/panda/src/display/displayRegion.h +++ b/panda/src/display/displayRegion.h @@ -33,6 +33,7 @@ #include "cycleDataStageWriter.h" #include "pipelineCycler.h" #include "config_display.h" +#include "lens.h" #include "plist.h" @@ -63,7 +64,7 @@ private: void operator = (const DisplayRegion ©); public: - ~DisplayRegion(); + virtual ~DisplayRegion(); void cleanup(); INLINE bool operator < (const DisplayRegion &other) const; @@ -88,6 +89,9 @@ PUBLISHED: void set_sort(int sort); INLINE int get_sort() const; + void set_stereo_channel(Lens::StereoChannel stereo_channel); + INLINE Lens::StereoChannel get_stereo_channel(); + INLINE void set_cube_map_index(int cube_map_index); INLINE int get_cube_map_index() const; @@ -114,6 +118,9 @@ public: INLINE CullResult *get_cull_result() const; INLINE SceneSetup *get_scene_setup() const; + virtual int get_screenshot_buffer_type() const; + virtual int get_draw_buffer_type() const; + private: class CData; @@ -155,6 +162,8 @@ private: bool _active; int _sort; + Lens::StereoChannel _stereo_channel; + int _draw_buffer_mask; int _cube_map_index; }; diff --git a/panda/src/display/drawableRegion.I b/panda/src/display/drawableRegion.I index 0d06b1ff63..7cfd717987 100644 --- a/panda/src/display/drawableRegion.I +++ b/panda/src/display/drawableRegion.I @@ -202,28 +202,3 @@ INLINE bool DrawableRegion:: is_any_clear_active() const { return (_flags & F_clear_all) != 0; } - -//////////////////////////////////////////////////////////////////// -// Function: DrawableRegion::get_draw_buffer_type -// Access: Public -// Description: Returns the RenderBuffer into which the GSG should -// issue draw commands. Normally, this is the back -// buffer for double-buffered windows, and the front -// buffer for single-buffered windows. -//////////////////////////////////////////////////////////////////// -INLINE int DrawableRegion:: -get_draw_buffer_type() const { - return _draw_buffer_type; -} - -//////////////////////////////////////////////////////////////////// -// Function: DrawableRegion::get_screenshot_buffer_type -// Access: Public, Virtual -// Description: Returns the RenderBuffer that should be used for -// capturing screenshots from this particular -// DrawableRegion. -//////////////////////////////////////////////////////////////////// -INLINE int DrawableRegion:: -get_screenshot_buffer_type() const { - return _screenshot_buffer_type; -} diff --git a/panda/src/display/drawableRegion.cxx b/panda/src/display/drawableRegion.cxx index dce859a96c..5f4117e790 100644 --- a/panda/src/display/drawableRegion.cxx +++ b/panda/src/display/drawableRegion.cxx @@ -17,3 +17,38 @@ //////////////////////////////////////////////////////////////////// #include "drawableRegion.h" + + +//////////////////////////////////////////////////////////////////// +// Function: DrawableRegion::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +DrawableRegion:: +~DrawableRegion() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DrawableRegion::get_screenshot_buffer_type +// Access: Public, Virtual +// Description: Returns the RenderBuffer that should be used for +// capturing screenshots from this particular +// DrawableRegion. +//////////////////////////////////////////////////////////////////// +int DrawableRegion:: +get_screenshot_buffer_type() const { + return _screenshot_buffer_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: DrawableRegion::get_draw_buffer_type +// Access: Public, Virtual +// Description: Returns the RenderBuffer into which the GSG should +// issue draw commands. Normally, this is the back +// buffer for double-buffered windows, and the front +// buffer for single-buffered windows. +//////////////////////////////////////////////////////////////////// +int DrawableRegion:: +get_draw_buffer_type() const { + return _draw_buffer_type; +} diff --git a/panda/src/display/drawableRegion.h b/panda/src/display/drawableRegion.h index 5105911b63..27cac87145 100644 --- a/panda/src/display/drawableRegion.h +++ b/panda/src/display/drawableRegion.h @@ -38,6 +38,7 @@ public: INLINE DrawableRegion(); INLINE DrawableRegion(const DrawableRegion ©); INLINE void operator = (const DrawableRegion ©); + virtual ~DrawableRegion(); INLINE void copy_clear_settings(const DrawableRegion ©); @@ -57,8 +58,8 @@ PUBLISHED: INLINE bool is_any_clear_active() const; public: - INLINE int get_screenshot_buffer_type() const; - INLINE int get_draw_buffer_type() const; + virtual int get_screenshot_buffer_type() const; + virtual int get_draw_buffer_type() const; protected: int _screenshot_buffer_type; diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index 0daa5b8f10..4681ed92a2 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -574,7 +574,7 @@ set_scene(SceneSetup *scene_setup) { if (_current_lens == (Lens *)NULL) { return false; } - return prepare_lens(); + return prepare_lens(scene_setup->get_display_region()->get_stereo_channel()); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 92eeb321b9..b377464af4 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -625,7 +625,7 @@ clear(DrawableRegion *clearable) { // false if it is not. //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: -prepare_lens() { +prepare_lens(Lens::StereoChannel stereo_channel) { return false; } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 8ab2d68d70..3a7dc3072d 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -25,7 +25,7 @@ #include "displayRegionStack.h" #include "lensStack.h" #include "preparedGraphicsObjects.h" - +#include "lens.h" #include "graphicsStateGuardianBase.h" #include "graphicsThreadingModel.h" #include "graphicsPipe.h" @@ -161,7 +161,7 @@ public: INLINE void clear(DisplayRegion *dr); virtual void prepare_display_region()=0; - virtual bool prepare_lens(); + virtual bool prepare_lens(Lens::StereoChannel stereo_channel); INLINE int force_normals(); INLINE int undo_force_normals(); diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index a0da72d959..48cfffc48b 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -609,7 +609,7 @@ prepare_display_region() { // false if it is not. //////////////////////////////////////////////////////////////////// bool DXGraphicsStateGuardian8:: -prepare_lens() { +prepare_lens(Lens::StereoChannel stereo_channel) { if (_current_lens == (Lens *)NULL) { return false; } @@ -619,7 +619,7 @@ prepare_lens() { } // Start with the projection matrix from the lens. - const LMatrix4f &lens_mat = _current_lens->get_projection_mat(); + const LMatrix4f &lens_mat = _current_lens->get_projection_mat(stereo_channel); // The projection matrix must always be left-handed Y-up internally, // to match DirectX's convention, even if our coordinate system of @@ -1301,7 +1301,8 @@ end_draw_primitives() { if (_vertex_data->is_vertex_transformed()) { // Restore the projection matrix that we wiped out above. - prepare_lens(); + _d3d_device->SetTransform(D3DTS_PROJECTION, + (D3DMATRIX*)_projection_mat.get_data()); } GraphicsStateGuardian::end_draw_primitives(); diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h index c9c48690b0..231cc287bd 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h @@ -67,7 +67,7 @@ public: virtual void do_clear(const RenderBuffer &buffer); virtual void prepare_display_region(); - virtual bool prepare_lens(); + virtual bool prepare_lens(Lens::StereoChannel stereo_channel); virtual bool begin_frame(); virtual bool begin_scene(); diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index e883951a01..16543abdd9 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -826,7 +826,7 @@ prepare_display_region() { // false if it is not. //////////////////////////////////////////////////////////////////// bool DXGraphicsStateGuardian9:: -prepare_lens() { +prepare_lens(Lens::StereoChannel stereo_channel) { if (_current_lens == (Lens *)NULL) { return false; } @@ -836,7 +836,7 @@ prepare_lens() { } // Start with the projection matrix from the lens. - const LMatrix4f &lens_mat = _current_lens->get_projection_mat(); + const LMatrix4f &lens_mat = _current_lens->get_projection_mat(stereo_channel); // The projection matrix must always be left-handed Y-up internally, // to match DirectX's convention, even if our coordinate system of @@ -1716,7 +1716,8 @@ end_draw_primitives() { if (_vertex_data->is_vertex_transformed()) { // Restore the projection matrix that we wiped out above. - prepare_lens(); + _d3d_device->SetTransform(D3DTS_PROJECTION, + (D3DMATRIX*)_projection_mat.get_data()); } GraphicsStateGuardian::end_draw_primitives(); diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h index 4b65f3efd7..0a14b13adc 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h @@ -103,7 +103,7 @@ public: virtual void do_clear(const RenderBuffer &buffer); virtual void prepare_display_region(); - virtual bool prepare_lens(); + virtual bool prepare_lens(Lens::StereoChannel stereo_channel); virtual bool begin_frame(); virtual bool begin_scene(); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 2e0dabb278..66b1c4218b 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -794,6 +794,7 @@ reset() { GLP(Disable)(GL_MULTISAMPLE); } + _stereo = ((get_properties().get_frame_buffer_mode() & FrameBufferProperties::FM_stereo) != 0); // Set up all the enabled/disabled flags to GL's known initial // values: everything off. @@ -984,6 +985,7 @@ prepare_display_region() { GLsizei width = GLsizei(w); GLsizei height = GLsizei(h); + set_draw_buffer(get_render_buffer(_actual_display_region->get_draw_buffer_type())); enable_scissor(true); GLP(Scissor)(x, y, width, height); GLP(Viewport)(x, y, width, height); @@ -1006,7 +1008,7 @@ prepare_display_region() { // false if it is not. //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: -prepare_lens() { +prepare_lens(Lens::StereoChannel stereo_channel) { if (_current_lens == (Lens *)NULL) { return false; } @@ -1015,7 +1017,7 @@ prepare_lens() { return false; } - const LMatrix4f &lens_mat = _current_lens->get_projection_mat(); + const LMatrix4f &lens_mat = _current_lens->get_projection_mat(stereo_channel); // The projection matrix must always be right-handed Y-up, even if // our coordinate system of choice is otherwise, because certain GL @@ -3732,19 +3734,35 @@ set_draw_buffer(const RenderBuffer &rb) { break; case RenderBuffer::T_front_right: - GLP(DrawBuffer)(GL_FRONT_RIGHT); + if (_stereo) { + GLP(DrawBuffer)(GL_FRONT_RIGHT); + } else { + GLP(DrawBuffer)(GL_FRONT); + } break; case RenderBuffer::T_front_left: - GLP(DrawBuffer)(GL_FRONT_LEFT); + if (_stereo) { + GLP(DrawBuffer)(GL_FRONT_LEFT); + } else { + GLP(DrawBuffer)(GL_FRONT); + } break; case RenderBuffer::T_back_right: - GLP(DrawBuffer)(GL_BACK_RIGHT); + if (_stereo) { + GLP(DrawBuffer)(GL_BACK_RIGHT); + } else { + GLP(DrawBuffer)(GL_BACK); + } break; case RenderBuffer::T_back_left: - GLP(DrawBuffer)(GL_BACK_LEFT); + if (_stereo) { + GLP(DrawBuffer)(GL_BACK_LEFT); + } else { + GLP(DrawBuffer)(GL_BACK); + } break; default: diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 4d70165f79..b9f04bc7f0 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -78,7 +78,7 @@ public: virtual void do_clear(const RenderBuffer &buffer); virtual void prepare_display_region(); - virtual bool prepare_lens(); + virtual bool prepare_lens(Lens::StereoChannel stereo_channel); virtual bool begin_frame(); virtual void end_frame(); @@ -282,6 +282,8 @@ protected: MM_alpha_mask = 0x0004, }; + bool _stereo; + int _multisample_mode; bool _line_smooth_enabled; bool _point_smooth_enabled; diff --git a/panda/src/gobj/lens.cxx b/panda/src/gobj/lens.cxx index 5a62ec9ec4..4f3a0539f2 100644 --- a/panda/src/gobj/lens.cxx +++ b/panda/src/gobj/lens.cxx @@ -66,6 +66,13 @@ operator = (const Lens ©) { _aspect_ratio = copy._aspect_ratio; _near_distance = copy._near_distance; _far_distance = copy._far_distance; + + _view_hpr = copy._view_hpr; + _view_vector = copy._view_vector; + _interocular_distance = copy._interocular_distance; + _convergence_distance = copy._convergence_distance; + _keystone = copy._keystone; + _user_flags = copy._user_flags; _comp_flags = 0; @@ -105,7 +112,8 @@ clear() { _view_hpr.set(0.0f, 0.0f, 0.0f); _view_vector.set(0.0f, 1.0f, 0.0f); _up_vector.set(0.0f, 0.0f, 1.0f); - _iod_offset = 0.0f; + _interocular_distance = 0.0f; + _convergence_distance = 0.0f; _keystone.set(0.0f, 0.0f); _user_flags = 0; @@ -432,7 +440,7 @@ set_view_hpr(const LVecBase3f &view_hpr) { _view_hpr = view_hpr; adjust_user_flags(UF_view_vector | UF_view_mat, UF_view_hpr); - adjust_comp_flags(CF_mat | CF_view_vector | CF_iod_offset, + adjust_comp_flags(CF_mat | CF_view_vector, CF_view_hpr); throw_change_event(); } @@ -465,7 +473,7 @@ set_view_vector(const LVector3f &view_vector, const LVector3f &up_vector) { _up_vector = up_vector; adjust_user_flags(UF_view_hpr | UF_view_mat, UF_view_vector); - adjust_comp_flags(CF_mat | CF_view_hpr | CF_iod_offset, + adjust_comp_flags(CF_mat | CF_view_hpr, CF_view_vector); throw_change_event(); } @@ -509,39 +517,71 @@ get_nodal_point() const { } //////////////////////////////////////////////////////////////////// -// Function: Lens::set_iod_offset +// Function: Lens::set_interocular_distance // Access: Published -// Description: Sets the amount by which the lens is shifted to the -// right, perpendicular to its view vector and up -// vector. This is normally used to shift one or both -// lens of a stereo camera to generate parallax. You -// can also simply set a complete transformation matrix -// (via set_view_mat()) that includes an arbitrary -// translation. +// Description: Sets the distance between the left and right eyes of +// a stereo camera. This distance is used to apply a +// stereo effect when the lens is rendered on a stereo +// display region. It only has an effect on a +// PerspectiveLens. +// +// Also see set_interocular_distance(), which relates. //////////////////////////////////////////////////////////////////// void Lens:: -set_iod_offset(float iod_offset) { - _iod_offset = iod_offset; - adjust_user_flags(UF_view_mat, - UF_iod_offset); - adjust_comp_flags(CF_mat | CF_view_hpr | CF_view_vector, - CF_iod_offset); +set_interocular_distance(float interocular_distance) { + _interocular_distance = interocular_distance; + if (_interocular_distance == 0.0f) { + adjust_user_flags(UF_interocular_distance, 0); + } else { + adjust_user_flags(0, UF_interocular_distance); + } + + adjust_comp_flags(CF_mat, 0); throw_change_event(); } //////////////////////////////////////////////////////////////////// -// Function: Lens::get_iod_offset +// Function: Lens::get_interocular_distance // Access: Published -// Description: Returns the aspect ratio of the Lens. This is -// determined based on the indicated film size; see -// set_film_size(). +// Description: See set_interocular_distance(). //////////////////////////////////////////////////////////////////// float Lens:: -get_iod_offset() const { - if ((_comp_flags & CF_iod_offset) == 0) { - ((Lens *)this)->compute_iod_offset(); +get_interocular_distance() const { + return _interocular_distance; +} + +//////////////////////////////////////////////////////////////////// +// Function: Lens::set_convergence_distance +// Access: Published +// Description: Sets the distance between the left and right eyes of +// a stereo camera. This distance is used to apply a +// stereo effect when the lens is rendered on a stereo +// display region. It only has an effect on a +// PerspectiveLens. +// +// Also see set_interocular_distance(), which relates. +//////////////////////////////////////////////////////////////////// +void Lens:: +set_convergence_distance(float convergence_distance) { + _convergence_distance = convergence_distance; + if (_convergence_distance == 0.0f) { + adjust_user_flags(UF_convergence_distance, 0); + } else { + adjust_user_flags(0, UF_convergence_distance); } - return _iod_offset; + + adjust_comp_flags(CF_mat, 0); + throw_change_event(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Lens::get_convergence_distance +// Access: Published +// Description: See set_convergence_distance(). +//////////////////////////////////////////////////////////////////// +float Lens:: +get_convergence_distance() const { + return _convergence_distance; } //////////////////////////////////////////////////////////////////// @@ -549,7 +589,7 @@ get_iod_offset() const { // Access: Published // Description: Sets an arbitrary transformation on the lens. This // replaces the individual transformation components -// like set_view_hpr() or set_iod_offset(). +// like set_view_hpr(). // // Setting a transformation here will have a slightly // different effect than putting one on the LensNode @@ -562,9 +602,11 @@ get_iod_offset() const { void Lens:: set_view_mat(const LMatrix4f &view_mat) { _lens_mat = view_mat; - adjust_user_flags(UF_view_vector | UF_view_hpr | UF_iod_offset, + adjust_user_flags(UF_view_vector | UF_view_hpr, UF_view_mat); - adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_lens_mat_inv | CF_view_hpr | CF_view_vector | CF_iod_offset, + adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | + CF_projection_mat_left_inv | CF_projection_mat_right_inv | + CF_lens_mat_inv | CF_view_hpr | CF_view_vector, CF_lens_mat); throw_change_event(); } @@ -590,8 +632,10 @@ get_view_mat() const { void Lens:: clear_view_mat() { _lens_mat = LMatrix4f::ident_mat(); - adjust_user_flags(0, UF_view_vector | UF_view_hpr | UF_iod_offset | UF_view_mat); - adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_lens_mat_inv | CF_view_hpr | CF_view_vector | CF_iod_offset, + adjust_user_flags(0, UF_view_vector | UF_view_hpr | UF_view_mat); + adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | + CF_projection_mat_left_inv | CF_projection_mat_right_inv | + CF_lens_mat_inv | CF_view_hpr | CF_view_vector, CF_lens_mat); throw_change_event(); } @@ -617,7 +661,9 @@ void Lens:: set_keystone(const LVecBase2f &keystone) { _keystone = keystone; adjust_user_flags(0, UF_keystone); - adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_film_mat | CF_film_mat_inv, 0); + adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | + CF_projection_mat_left_inv | CF_projection_mat_right_inv | + CF_film_mat | CF_film_mat_inv, 0); throw_change_event(); } @@ -630,7 +676,9 @@ void Lens:: clear_keystone() { _keystone.set(0.0f, 0.0f); adjust_user_flags(UF_keystone, 0); - adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_film_mat | CF_film_mat_inv, 0); + adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | + CF_projection_mat_left_inv | CF_projection_mat_right_inv | + CF_film_mat | CF_film_mat_inv, 0); throw_change_event(); } @@ -642,9 +690,9 @@ clear_keystone() { // PerspectiveLens, but it may be called for other kinds // of lenses as well. // -// The frustum will be rooted at the origin (or offset -// by iod_offset, or by whatever translation might have -// been specified in a previous call to set_view_mat). +// The frustum will be rooted at the origin (or by +// whatever translation might have been specified in a +// previous call to set_view_mat). // // It is legal for the four points not to be arranged in // a rectangle; if this is the case, the frustum will be @@ -699,8 +747,7 @@ set_frustum_from_corners(const LVecBase3f &ul, const LVecBase3f &ur, int flags) { // We'll need to know the pre-existing eyepoint translation from the // center, so we can preserve it in the new frustum. This is - // usually just a shift along the x axis, if anything at all, for - // the iod offset, but it could be an arbitrary vector. + // usually (0, 0, 0), but it could be an arbitrary vector. const LMatrix4f &lens_mat_inv = get_lens_mat_inv(); LVector3f eye_offset; lens_mat_inv.get_row3(eye_offset, 3); @@ -1018,22 +1065,59 @@ make_bounds() const { // nonlinear. //////////////////////////////////////////////////////////////////// const LMatrix4f &Lens:: -get_projection_mat() const { +get_projection_mat(StereoChannel channel) const { if ((_comp_flags & CF_projection_mat) == 0) { ((Lens *)this)->compute_projection_mat(); } + + switch (channel) { + case SC_left: + return _projection_mat_left; + case SC_right: + return _projection_mat_right; + case SC_both: + return _projection_mat; + } + return _projection_mat; } //////////////////////////////////////////////////////////////////// // Function: Lens::get_projection_mat_inv -// Access: Public +// Access: Published // Description: Returns the matrix that transforms from a 2-d point // on the film to a 3-d vector in space, if such a // matrix exists. //////////////////////////////////////////////////////////////////// const LMatrix4f &Lens:: -get_projection_mat_inv() const { +get_projection_mat_inv(StereoChannel stereo_channel) const { + switch (stereo_channel) { + case SC_left: + { + if ((_comp_flags & CF_projection_mat_left_inv) == 0) { + Lens *non_const = (Lens *)this; + const LMatrix4f &projection_mat_left = get_projection_mat(SC_left); + non_const->_projection_mat_left_inv.invert_from(projection_mat_left); + non_const->adjust_comp_flags(0, CF_projection_mat_left_inv); + } + } + return _projection_mat_left_inv; + + case SC_right: + { + if ((_comp_flags & CF_projection_mat_right_inv) == 0) { + Lens *non_const = (Lens *)this; + const LMatrix4f &projection_mat_right = get_projection_mat(SC_right); + non_const->_projection_mat_right_inv.invert_from(projection_mat_right); + non_const->adjust_comp_flags(0, CF_projection_mat_right_inv); + } + } + return _projection_mat_right_inv; + + case SC_both: + break; + } + if ((_comp_flags & CF_projection_mat_inv) == 0) { Lens *non_const = (Lens *)this; const LMatrix4f &projection_mat = get_projection_mat(); @@ -1043,6 +1127,68 @@ get_projection_mat_inv() const { return _projection_mat_inv; } +//////////////////////////////////////////////////////////////////// +// Function: Lens::get_film_mat +// Access: Published +// Description: Returns the matrix that transforms from a point +// behind the lens to a point on the film. +//////////////////////////////////////////////////////////////////// +const LMatrix4f &Lens:: +get_film_mat() const { + if ((_comp_flags & CF_film_mat) == 0) { + ((Lens *)this)->compute_film_mat(); + } + return _film_mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: Lens::get_film_mat_inv +// Access: Published +// Description: Returns the matrix that transforms from a point on +// the film to a point behind the lens. +//////////////////////////////////////////////////////////////////// +const LMatrix4f &Lens:: +get_film_mat_inv() const { + if ((_comp_flags & CF_film_mat_inv) == 0) { + Lens *non_const = (Lens *)this; + const LMatrix4f &film_mat = get_film_mat(); + non_const->_film_mat_inv.invert_from(film_mat); + non_const->adjust_comp_flags(0, CF_film_mat_inv); + } + return _film_mat_inv; +} + +//////////////////////////////////////////////////////////////////// +// Function: Lens::get_lens_mat +// Access: Published +// Description: Returns the matrix that transforms from a point +// in front of the lens to a point in space. +//////////////////////////////////////////////////////////////////// +const LMatrix4f &Lens:: +get_lens_mat() const { + if ((_comp_flags & CF_lens_mat) == 0) { + ((Lens *)this)->compute_lens_mat(); + } + return _lens_mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: Lens::get_lens_mat_inv +// Access: Published +// Description: Returns the matrix that transforms from a point in +// space to a point in front of the lens. +//////////////////////////////////////////////////////////////////// +const LMatrix4f &Lens:: +get_lens_mat_inv() const { + if ((_comp_flags & CF_lens_mat_inv) == 0) { + Lens *non_const = (Lens *)this; + const LMatrix4f &lens_mat = get_lens_mat(); + non_const->_lens_mat_inv.invert_from(lens_mat); + non_const->adjust_comp_flags(0, CF_lens_mat_inv); + } + return _lens_mat_inv; +} + //////////////////////////////////////////////////////////////////// // Function: Lens::output // Access: Published, Virtual @@ -1091,68 +1237,6 @@ throw_change_event() { } } -//////////////////////////////////////////////////////////////////// -// Function: Lens::get_film_mat -// Access: Protected -// Description: Returns the matrix that transforms from a point -// behind the lens to a point on the film. -//////////////////////////////////////////////////////////////////// -const LMatrix4f &Lens:: -get_film_mat() const { - if ((_comp_flags & CF_film_mat) == 0) { - ((Lens *)this)->compute_film_mat(); - } - return _film_mat; -} - -//////////////////////////////////////////////////////////////////// -// Function: Lens::get_film_mat_inv -// Access: Protected -// Description: Returns the matrix that transforms from a point on -// the film to a point behind the lens. -//////////////////////////////////////////////////////////////////// -const LMatrix4f &Lens:: -get_film_mat_inv() const { - if ((_comp_flags & CF_film_mat_inv) == 0) { - Lens *non_const = (Lens *)this; - const LMatrix4f &film_mat = get_film_mat(); - non_const->_film_mat_inv.invert_from(film_mat); - non_const->adjust_comp_flags(0, CF_film_mat_inv); - } - return _film_mat_inv; -} - -//////////////////////////////////////////////////////////////////// -// Function: Lens::get_lens_mat -// Access: Protected -// Description: Returns the matrix that transforms from a point -// in front of the lens to a point in space. -//////////////////////////////////////////////////////////////////// -const LMatrix4f &Lens:: -get_lens_mat() const { - if ((_comp_flags & CF_lens_mat) == 0) { - ((Lens *)this)->compute_lens_mat(); - } - return _lens_mat; -} - -//////////////////////////////////////////////////////////////////// -// Function: Lens::get_lens_mat_inv -// Access: Protected -// Description: Returns the matrix that transforms from a point in -// space to a point in front of the lens. -//////////////////////////////////////////////////////////////////// -const LMatrix4f &Lens:: -get_lens_mat_inv() const { - if ((_comp_flags & CF_lens_mat_inv) == 0) { - Lens *non_const = (Lens *)this; - const LMatrix4f &lens_mat = get_lens_mat(); - non_const->_lens_mat_inv.invert_from(lens_mat); - non_const->adjust_comp_flags(0, CF_lens_mat_inv); - } - return _lens_mat_inv; -} - //////////////////////////////////////////////////////////////////// // Function: Lens::extrude_impl // Access: Protected, Virtual @@ -1411,23 +1495,6 @@ compute_view_vector() { adjust_comp_flags(0, CF_view_vector); } -//////////////////////////////////////////////////////////////////// -// Function: Lens::compute_iod_offset -// Access: Protected, Virtual -// Description: Computes the IOD offset: the translation along the -// "right" axis. -//////////////////////////////////////////////////////////////////// -void Lens:: -compute_iod_offset() { - if ((_user_flags & UF_iod_offset) == 0) { - const LMatrix4f &lens_mat_inv = get_lens_mat_inv(); - LVector3f translate; - lens_mat_inv.get_row3(translate, 3); - _iod_offset = -translate.dot(LVector3f::right(_cs)); - } - adjust_comp_flags(0, CF_iod_offset); -} - //////////////////////////////////////////////////////////////////// // Function: Lens::compute_projection_mat // Access: Protected, Virtual @@ -1436,9 +1503,15 @@ compute_iod_offset() { //////////////////////////////////////////////////////////////////// void Lens:: compute_projection_mat() { - _projection_mat = LMatrix4f::ident_mat(); - _projection_mat_inv = _projection_mat; - adjust_comp_flags(0, CF_projection_mat | CF_projection_mat_inv); + _projection_mat = + _projection_mat_left = + _projection_mat_right = + _projection_mat_inv = + _projection_mat_left_inv = + _projection_mat_right_inv = + LMatrix4f::ident_mat(); + adjust_comp_flags(0, CF_projection_mat | CF_projection_mat_inv | + CF_projection_mat_left_inv |CF_projection_mat_right_inv); } //////////////////////////////////////////////////////////////////// @@ -1498,11 +1571,6 @@ compute_lens_mat() { } else { _lens_mat = LMatrix4f::ident_mat(); } - - if ((_user_flags & UF_iod_offset) != 0) { - LVector3f iod_vector = _iod_offset * LVector3f::right(_cs); - _lens_mat = LMatrix4f::translate_mat(iod_vector) * _lens_mat; - } } adjust_comp_flags(CF_lens_mat_inv, CF_lens_mat); diff --git a/panda/src/gobj/lens.h b/panda/src/gobj/lens.h index 26e4a13d1f..09f5bfaf3e 100644 --- a/panda/src/gobj/lens.h +++ b/panda/src/gobj/lens.h @@ -49,6 +49,12 @@ public: void operator = (const Lens ©); PUBLISHED: + enum StereoChannel { + SC_left = 0x01, + SC_right = 0x02, + SC_both = 0x03, // == SC_left | SC_right + }; + virtual PT(Lens) make_copy() const=0; INLINE bool extrude(const LPoint2f &point2d, @@ -107,8 +113,11 @@ PUBLISHED: const LVector3f &get_view_vector() const; const LVector3f &get_up_vector() const; LPoint3f get_nodal_point() const; - void set_iod_offset(float offset); - float get_iod_offset() const; + + void set_interocular_distance(float interocular_distance); + float get_interocular_distance() const; + void set_convergence_distance(float convergence_distance); + float get_convergence_distance() const; void set_view_mat(const LMatrix4f &view_mat); const LMatrix4f &get_view_mat() const; @@ -142,8 +151,14 @@ PUBLISHED: virtual PT(BoundingVolume) make_bounds() const; - const LMatrix4f &get_projection_mat() const; - const LMatrix4f &get_projection_mat_inv() const; + const LMatrix4f &get_projection_mat(StereoChannel channel = SC_both) const; + const LMatrix4f &get_projection_mat_inv(StereoChannel channel = SC_both) const; + + const LMatrix4f &get_film_mat() const; + const LMatrix4f &get_film_mat_inv() const; + + const LMatrix4f &get_lens_mat() const; + const LMatrix4f &get_lens_mat_inv() const; virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level = 0) const; @@ -157,12 +172,6 @@ protected: void throw_change_event(); - const LMatrix4f &get_film_mat() const; - const LMatrix4f &get_film_mat_inv() const; - - const LMatrix4f &get_lens_mat() const; - const LMatrix4f &get_lens_mat_inv() const; - virtual bool extrude_impl(const LPoint3f &point2d, LPoint3f &near_point, LPoint3f &far_point) const; virtual bool extrude_vec_impl(const LPoint3f &point2d, LVector3f &vec) const; @@ -174,7 +183,6 @@ protected: virtual void compute_aspect_ratio(); virtual void compute_view_hpr(); virtual void compute_view_vector(); - virtual void compute_iod_offset(); virtual void compute_projection_mat(); virtual void compute_film_mat(); virtual void compute_lens_mat(); @@ -206,26 +214,30 @@ protected: LVecBase3f _view_hpr; LVector3f _view_vector, _up_vector; - float _iod_offset; + float _interocular_distance; + float _convergence_distance; LVecBase2f _keystone; LMatrix4f _film_mat, _film_mat_inv; LMatrix4f _lens_mat, _lens_mat_inv; LMatrix4f _projection_mat, _projection_mat_inv; + LMatrix4f _projection_mat_left, _projection_mat_left_inv; + LMatrix4f _projection_mat_right, _projection_mat_right_inv; enum UserFlags { // Parameters the user may have explicitly specified. - UF_film_width = 0x0001, - UF_film_height = 0x0002, - UF_focal_length = 0x0004, - UF_hfov = 0x0008, - UF_vfov = 0x0010, - UF_aspect_ratio = 0x0020, - UF_view_hpr = 0x0040, - UF_view_vector = 0x0080, - UF_iod_offset = 0x0100, - UF_view_mat = 0x0200, - UF_keystone = 0x0400, + UF_film_width = 0x0001, + UF_film_height = 0x0002, + UF_focal_length = 0x0004, + UF_hfov = 0x0008, + UF_vfov = 0x0010, + UF_aspect_ratio = 0x0020, + UF_view_hpr = 0x0040, + UF_view_vector = 0x0080, + UF_interocular_distance = 0x0100, + UF_convergence_distance = 0x0200, + UF_view_mat = 0x0400, + UF_keystone = 0x0800, }; enum CompFlags { @@ -236,15 +248,16 @@ protected: CF_lens_mat_inv = 0x0008, CF_projection_mat = 0x0010, CF_projection_mat_inv = 0x0020, - CF_mat = 0x003f, // all of the above. + CF_projection_mat_left_inv = 0x0040, + CF_projection_mat_right_inv = 0x0080, + CF_mat = 0x00ff, // all of the above. - CF_focal_length = 0x0040, - CF_fov = 0x0080, CF_film_size = 0x0100, CF_aspect_ratio = 0x0200, CF_view_hpr = 0x0400, CF_view_vector = 0x0800, - CF_iod_offset = 0x1000, + CF_focal_length = 0x1000, + CF_fov = 0x2000, }; short _user_flags; short _comp_flags; diff --git a/panda/src/gobj/orthographicLens.cxx b/panda/src/gobj/orthographicLens.cxx index 695354cbf3..e4881e0d8e 100644 --- a/panda/src/gobj/orthographicLens.cxx +++ b/panda/src/gobj/orthographicLens.cxx @@ -119,9 +119,11 @@ compute_projection_mat() { canonical = LMatrix4f::ident_mat(); } - _projection_mat = get_lens_mat_inv() * canonical * get_film_mat(); - adjust_comp_flags(CF_projection_mat_inv, + _projection_mat_left = _projection_mat_right = _projection_mat; + + adjust_comp_flags(CF_projection_mat_inv | CF_projection_mat_left_inv | + CF_projection_mat_right_inv, CF_projection_mat); } diff --git a/panda/src/gobj/perspectiveLens.cxx b/panda/src/gobj/perspectiveLens.cxx index aeb2b852ff..c99d583ab7 100644 --- a/panda/src/gobj/perspectiveLens.cxx +++ b/panda/src/gobj/perspectiveLens.cxx @@ -76,8 +76,8 @@ compute_projection_mat() { float a = (fFar + fNear); float b = -2.0f * fFar * fNear; - a/=far_minus_near; - b/=far_minus_near; + a /= far_minus_near; + b /= far_minus_near; LMatrix4f canonical; switch (cs) { @@ -115,9 +115,29 @@ compute_projection_mat() { canonical = LMatrix4f::ident_mat(); } - _projection_mat = get_lens_mat_inv() * canonical * get_film_mat(); - adjust_comp_flags(CF_projection_mat_inv, + + if ((_user_flags & UF_interocular_distance) == 0) { + _projection_mat_left = _projection_mat_right = _projection_mat; + + } else { + // Compute the left and right projection matrices in case this + // lens is assigned to a stereo DisplayRegion. + + LVector3f iod = _interocular_distance * 0.5f * LVector3f::left(_cs); + _projection_mat_left = get_lens_mat_inv() * LMatrix4f::translate_mat(-iod) * canonical * get_film_mat(); + _projection_mat_right = get_lens_mat_inv() * LMatrix4f::translate_mat(iod) * canonical * get_film_mat(); + + if (_user_flags & UF_convergence_distance) { + nassertv(_convergence_distance != 0.0f); + LVector3f cd = (0.25f / _convergence_distance) * LVector3f::left(_cs); + _projection_mat_left *= LMatrix4f::translate_mat(cd); + _projection_mat_right *= LMatrix4f::translate_mat(-cd); + } + } + + adjust_comp_flags(CF_projection_mat_inv | CF_projection_mat_left_inv | + CF_projection_mat_right_inv, CF_projection_mat); }