Implement wide side-by-side mode, using adjusted aspect ratio in projection matrix.

Use optimal framebuffer size for side-by-side modes.
This commit is contained in:
Christopher Bruns 2016-09-11 13:42:41 -04:00 committed by Christoph Oelckers
parent 9a257ac158
commit 1f79e23d5b
8 changed files with 87 additions and 24 deletions

View file

@ -57,6 +57,7 @@
#include "gl/shaders/gl_colormapshader.h" #include "gl/shaders/gl_colormapshader.h"
#include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_lensshader.h"
#include "gl/shaders/gl_presentshader.h" #include "gl/shaders/gl_presentshader.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/textures/gl_texture.h" #include "gl/textures/gl_texture.h"
#include "gl/textures/gl_translate.h" #include "gl/textures/gl_translate.h"
#include "gl/textures/gl_material.h" #include "gl/textures/gl_material.h"
@ -273,6 +274,8 @@ void FGLRenderer::SetOutputViewport(GL_IRECT *bounds)
mSceneViewport.top += mOutputLetterbox.top; mSceneViewport.top += mOutputLetterbox.top;
} }
} }
s3d::Stereo3DMode::getCurrentMode().AdjustViewports();
} }
//=========================================================================== //===========================================================================

View file

@ -801,7 +801,6 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo
{ {
const s3d::EyePose * eye = stereo3dMode.getEyePose(eye_ix); const s3d::EyePose * eye = stereo3dMode.getEyePose(eye_ix);
eye->SetUp(); eye->SetUp();
// TODO: stereo specific viewport - needed when implementing side-by-side modes etc.
SetOutputViewport(bounds); SetOutputViewport(bounds);
Set3DViewport(mainview); Set3DViewport(mainview);
mDrawingScene2D = true; mDrawingScene2D = true;

View file

@ -39,6 +39,36 @@
namespace s3d { namespace s3d {
void SideBySideBase::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int leftWidth = GLRenderer->mOutputLetterbox.width / 2;
int rightWidth = GLRenderer->mOutputLetterbox.width - leftWidth;
GL_IRECT leftHalfScreen = GLRenderer->mOutputLetterbox;
leftHalfScreen.width = leftWidth;
GL_IRECT rightHalfScreen = GLRenderer->mOutputLetterbox;
rightHalfScreen.width = rightWidth;
rightHalfScreen.left += leftWidth;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(leftHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(rightHalfScreen, true);
}
// AdjustViewports() is called from within FLGRenderer::SetOutputViewport(...)
void SideBySideBase::AdjustViewports() const
{
// Change size of renderbuffer, and align to screen
GLRenderer->mSceneViewport.width /= 2;
GLRenderer->mSceneViewport.left /= 2;
GLRenderer->mScreenViewport.width /= 2;
GLRenderer->mScreenViewport.left /= 2;
}
/* static */ /* static */
const SideBySideSquished& SideBySideSquished::getInstance(float ipd) const SideBySideSquished& SideBySideSquished::getInstance(float ipd)
@ -54,26 +84,18 @@ SideBySideSquished::SideBySideSquished(double ipdMeters)
eye_ptrs.Push(&rightEye); eye_ptrs.Push(&rightEye);
} }
void SideBySideSquished::Present() const /* static */
const SideBySideFull& SideBySideFull::getInstance(float ipd)
{ {
GLRenderer->mBuffers->BindOutputFB(); static SideBySideFull instance(ipd);
GLRenderer->ClearBorders(); return instance;
// Compute screen regions to use for left and right eye views
int leftWidth = GLRenderer->mOutputLetterbox.width/2;
int rightWidth = GLRenderer->mOutputLetterbox.width - leftWidth;
GL_IRECT leftHalfScreen = GLRenderer->mOutputLetterbox;
leftHalfScreen.width = leftWidth;
GL_IRECT rightHalfScreen = GLRenderer->mOutputLetterbox;
rightHalfScreen.width = rightWidth;
rightHalfScreen.left += leftWidth;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(leftHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(rightHalfScreen, true);
} }
SideBySideFull::SideBySideFull(double ipdMeters)
: leftEye(ipdMeters), rightEye(ipdMeters)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
} /* namespace s3d */ } /* namespace s3d */

View file

@ -44,18 +44,49 @@
namespace s3d { namespace s3d {
class SideBySideSquished : public Stereo3DMode class SideBySideBase : public Stereo3DMode
{
public:
void Present() const override;
virtual void AdjustViewports() const override;
};
class SideBySideSquished : public SideBySideBase
{ {
public: public:
static const SideBySideSquished& getInstance(float ipd); static const SideBySideSquished& getInstance(float ipd);
SideBySideSquished(double ipdMeters); SideBySideSquished(double ipdMeters);
void Present() const override;
private: private:
LeftEyePose leftEye; LeftEyePose leftEye;
RightEyePose rightEye; RightEyePose rightEye;
}; };
class SBSFLeftEyePose : public LeftEyePose {
public:
SBSFLeftEyePose(double ipdMeters) : LeftEyePose(ipdMeters) {}
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override {
return LeftEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio);
}
};
class SBSFRightEyePose : public RightEyePose {
public:
SBSFRightEyePose(double ipdMeters) : RightEyePose(ipdMeters) {}
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override {
return RightEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio);
}
};
class SideBySideFull : public SideBySideBase
{
public:
static const SideBySideFull& getInstance(float ipd);
SideBySideFull(double ipdMeters);
private:
SBSFLeftEyePose leftEye;
SBSFRightEyePose rightEye;
};
} /* namespace s3d */ } /* namespace s3d */

View file

@ -31,6 +31,7 @@
#include <cstring> // needed for memcpy on linux, which is needed by VSMatrix copy ctor #include <cstring> // needed for memcpy on linux, which is needed by VSMatrix copy ctor
#include "tarray.h" #include "tarray.h"
#include "gl/data/gl_matrix.h" #include "gl/data/gl_matrix.h"
#include "gl/renderer/gl_renderer.h"
/* stereoscopic 3D API */ /* stereoscopic 3D API */
@ -78,6 +79,7 @@ public:
virtual void TearDown() const {}; virtual void TearDown() const {};
virtual bool IsMono() const { return false; } virtual bool IsMono() const { return false; }
virtual void AdjustViewports() const {};
virtual void Present() const = 0; virtual void Present() const = 0;
protected: protected:

View file

@ -72,7 +72,9 @@ const Stereo3DMode& Stereo3DMode::getCurrentMode()
case 2: case 2:
setCurrentMode(RedCyan::getInstance(vr_ipd)); setCurrentMode(RedCyan::getInstance(vr_ipd));
break; break;
// TODO: missing index 3 for not-yet-implemented side-by-side mode, to match values from GZ3Doom case 3:
setCurrentMode(SideBySideFull::getInstance(vr_ipd));
break;
case 4: case 4:
setCurrentMode(SideBySideSquished::getInstance(vr_ipd)); setCurrentMode(SideBySideSquished::getInstance(vr_ipd));
break; break;
@ -93,7 +95,9 @@ const Stereo3DMode& Stereo3DMode::getCurrentMode()
// TODO: 8: Oculus Rift // TODO: 8: Oculus Rift
case 9: case 9:
setCurrentMode(AmberBlue::getInstance(vr_ipd)); setCurrentMode(AmberBlue::getInstance(vr_ipd));
break; case 0: break;
// TODO: 10: HTC Vive/OpenVR
case 0:
default: default:
setCurrentMode(MonoView::getInstance()); setCurrentMode(MonoView::getInstance());
break; break;

View file

@ -2701,6 +2701,7 @@ OPTVAL_REDCYAN = "Red/Cyan";
OPTVAL_AMBERBLUE = "Amber/Blue"; OPTVAL_AMBERBLUE = "Amber/Blue";
OPTVAL_LEFTEYE = "Left Eye"; OPTVAL_LEFTEYE = "Left Eye";
OPTVAL_RIGHTEYE = "Right Eye"; OPTVAL_RIGHTEYE = "Right Eye";
OPTVAL_SBSFULL = "Side-by-side Full";
OPTVAL_SBSNARROW = "Side-by-side Narrow"; OPTVAL_SBSNARROW = "Side-by-side Narrow";
OPTVAL_QUADBUFFERED = "Quad-buffered"; OPTVAL_QUADBUFFERED = "Quad-buffered";
OPTVAL_UNCHARTED2 = "Uncharted 2"; OPTVAL_UNCHARTED2 = "Uncharted 2";

View file

@ -167,6 +167,7 @@ OptionValue VRMode
1, "$OPTVAL_GREENMAGENTA" 1, "$OPTVAL_GREENMAGENTA"
2, "$OPTVAL_REDCYAN" 2, "$OPTVAL_REDCYAN"
9, "$OPTVAL_AMBERBLUE" 9, "$OPTVAL_AMBERBLUE"
3, "$OPTVAL_SBSFULL"
4, "$OPTVAL_SBSNARROW" 4, "$OPTVAL_SBSNARROW"
5, "$OPTVAL_LEFTEYE" 5, "$OPTVAL_LEFTEYE"
6, "$OPTVAL_RIGHTEYE" 6, "$OPTVAL_RIGHTEYE"