- refactored the entire Stereo3D system to get rid of the class hierarchy and all its ugly implications.

Ultimately all this needs is a small data table describing the geometric properties of each mode and a single Present function that calls the mode specific variants.
Code size got reduced from 50kb to less than 20kb with proper separation of the generic parts from the OpenGL parts.
This commit is contained in:
Christoph Oelckers 2018-06-24 13:39:14 +02:00
parent 036307927a
commit c3d5b960ee
29 changed files with 687 additions and 1723 deletions

View file

@ -697,7 +697,6 @@ file( GLOB HEADER_FILES
hwrenderer/dynlights/*.h
hwrenderer/postprocessing/*.h
hwrenderer/scene/*.h
hwrenderer/stereo3d/*.h
hwrenderer/textures/*.h
hwrenderer/utility/*.h
gl/*.h
@ -706,7 +705,6 @@ file( GLOB HEADER_FILES
gl/models/*.h
gl/renderer/*.h
gl/scene/*.h
gl/stereo3d/*.h
gl/shaders/*.h
gl/system/*.h
gl/textures/*.h
@ -1055,16 +1053,10 @@ set (PCH_SOURCES
gl/renderer/gl_lightdata.cpp
gl/renderer/gl_postprocess.cpp
gl/renderer/gl_postprocessstate.cpp
gl/renderer/gl_stereo3d.cpp
gl/shaders/gl_shader.cpp
gl/shaders/gl_shaderprogram.cpp
gl/shaders/gl_postprocessshader.cpp
gl/stereo3d/gl_stereo3d.cpp
gl/stereo3d/gl_stereo_cvars.cpp
gl/stereo3d/gl_stereo_leftright.cpp
gl/stereo3d/gl_anaglyph.cpp
gl/stereo3d/gl_quadstereo.cpp
gl/stereo3d/gl_sidebyside3d.cpp
gl/stereo3d/gl_interleaved3d.cpp
gl_load/gl_interface.cpp
gl/system/gl_framebuffer.cpp
gl/system/gl_debug.cpp
@ -1074,7 +1066,6 @@ set (PCH_SOURCES
hwrenderer/data/flatvertices.cpp
hwrenderer/dynlights/hw_aabbtree.cpp
hwrenderer/dynlights/hw_shadowmap.cpp
hwrenderer/stereo3d/hw_eyepose.cpp
hwrenderer/scene/hw_skydome.cpp
hwrenderer/postprocessing/hw_postprocess_cvars.cpp
hwrenderer/postprocessing/hw_postprocessshader.cpp
@ -1094,6 +1085,7 @@ set (PCH_SOURCES
hwrenderer/utility/hw_cvars.cpp
hwrenderer/utility/hw_lighting.cpp
hwrenderer/utility/hw_shaderpatcher.cpp
hwrenderer/utility/hw_vrmodes.cpp
menu/joystickmenu.cpp
menu/loadsavemenu.cpp
@ -1424,7 +1416,6 @@ source_group("Hardware Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SO
source_group("Hardware Renderer\\Postprocessing" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/postprocessing/.+")
source_group("Hardware Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/renderer/.+")
source_group("Hardware Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/scene/.+")
source_group("Hardware Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/stereo3d/.+")
source_group("Hardware Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/shaders/.+")
source_group("Hardware Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/system/.+")
source_group("Hardware Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/textures/.+")
@ -1436,7 +1427,6 @@ source_group("OpenGL Renderer\\Dynamic Lights" REGULAR_EXPRESSION "^${CMAKE_CURR
source_group("OpenGL Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/models/.+")
source_group("OpenGL Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/renderer/.+")
source_group("OpenGL Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/scene/.+")
source_group("OpenGL Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/stereo3d/.+")
source_group("OpenGL Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/shaders/.+")
source_group("OpenGL Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/system/.+")
source_group("OpenGL Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/textures/.+")

View file

@ -47,8 +47,8 @@
#include "hwrenderer/postprocessing/hw_lensshader.h"
#include "hwrenderer/postprocessing/hw_fxaashader.h"
#include "hwrenderer/postprocessing/hw_presentshader.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "gl/shaders/gl_postprocessshaderinstance.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/textures/gl_hwtexture.h"
#include "r_videoscale.h"
@ -654,18 +654,18 @@ void FGLRenderer::ApplyFXAA()
void FGLRenderer::Flush()
{
const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode();
auto vrmode = VRMode::GetVRMode(true);
const auto &mSceneViewport = screen->mSceneViewport;
const auto &mScreenViewport = screen->mScreenViewport;
if (stereo3dMode.IsMono())
if (vrmode->mEyeCount == 1)
{
CopyToBackbuffer(nullptr, true);
}
else
{
// Render 2D to eye textures
for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix)
for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix)
{
FGLDebug::PushGroup("Eye2D");
mBuffers->BindEyeFB(eye_ix);
@ -678,7 +678,9 @@ void FGLRenderer::Flush()
FGLPostProcessState savedState;
FGLDebug::PushGroup("PresentEyes");
stereo3dMode.Present();
// Note: This here is the ONLY place in the entire engine where the OpenGL dependent parts of the Stereo3D code need to be dealt with.
// There's absolutely no need to create a overly complex class hierarchy for just this.
GLRenderer->PresentStereo();
FGLDebug::PopGroup();
}
}

View file

@ -61,7 +61,6 @@
#include "hwrenderer/postprocessing/hw_present3dRowshader.h"
#include "hwrenderer/postprocessing/hw_shadowmapshader.h"
#include "gl/shaders/gl_postprocessshaderinstance.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/textures/gl_samplers.h"
#include "gl/dynlights/gl_lightbuffer.h"
#include "r_videoscale.h"

View file

@ -127,6 +127,7 @@ public:
void SetupLevel();
void ResetSWScene();
void PresentStereo();
void RenderScreenQuad();
void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D);
void AmbientOccludeScene(float m5);

View file

@ -0,0 +1,364 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo3d.cpp
** Stereoscopic 3D API
**
*/
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderbuffers.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/renderer/gl_postprocessstate.h"
#include "hwrenderer/postprocessing/hw_presentshader.h"
#include "hwrenderer/postprocessing/hw_present3dRowshader.h"
EXTERN_CVAR(Int, vr_mode)
EXTERN_CVAR(Float, vid_saturation)
EXTERN_CVAR(Float, vid_brightness)
EXTERN_CVAR(Float, vid_contrast)
EXTERN_CVAR(Int, gl_satformula)
//==========================================================================
//
//
//
//==========================================================================
static void PresentAnaglyph(bool r, bool g, bool b)
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
glColorMask(r, g, b, 1);
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glColorMask(!r, !g, !b, 1);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glColorMask(1, 1, 1, 1);
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentSideBySide()
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int leftWidth = screen->mOutputLetterbox.width / 2;
int rightWidth = screen->mOutputLetterbox.width - leftWidth;
IntRect leftHalfScreen = screen->mOutputLetterbox;
leftHalfScreen.width = leftWidth;
IntRect rightHalfScreen = screen->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);
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentTopBottom()
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int topHeight = screen->mOutputLetterbox.height / 2;
int bottomHeight = screen->mOutputLetterbox.height - topHeight;
IntRect topHalfScreen = screen->mOutputLetterbox;
topHalfScreen.height = topHeight;
topHalfScreen.top = topHeight;
IntRect bottomHalfScreen = screen->mOutputLetterbox;
bottomHalfScreen.height = bottomHeight;
bottomHalfScreen.top = 0;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(topHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(bottomHalfScreen, true);
}
//==========================================================================
//
//
//
//==========================================================================
static void prepareInterleavedPresent(FPresentShaderBase& shader)
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Bind each eye texture, for composition in the shader
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->mBuffers->BindEyeTexture(1, 1);
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
const IntRect& box = screen->mOutputLetterbox;
glViewport(box.left, box.top, box.width, box.height);
shader.Bind(NOQUEUE);
if (GLRenderer->framebuffer->IsHWGammaActive())
{
shader.Uniforms->InvGamma = 1.0f;
shader.Uniforms->Contrast = 1.0f;
shader.Uniforms->Brightness = 0.0f;
shader.Uniforms->Saturation = 1.0f;
}
else
{
shader.Uniforms->InvGamma = 1.0f / clamp<float>(Gamma, 0.1f, 4.f);
shader.Uniforms->Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
shader.Uniforms->Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
shader.Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.0f);
shader.Uniforms->GrayFormula = static_cast<int>(gl_satformula);
}
shader.Uniforms->Scale = {
screen->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(),
screen->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight()
};
shader.Uniforms.Set();
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentColumnInterleaved()
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dColumnShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
// Todo:
//auto clientoffset = screen->GetClientOffset();
//auto windowHOffset = clientoffset.X % 2;
int windowHOffset = 0;
GLRenderer->mPresent3dColumnShader->Uniforms->WindowPositionParity = windowHOffset;
GLRenderer->mPresent3dColumnShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentRowInterleaved()
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dRowShader);
// Todo:
//auto clientoffset = screen->GetClientOffset();
//auto windowVOffset = clientoffset.Y % 2;
int windowVOffset = 0;
GLRenderer->mPresent3dRowShader->Uniforms->WindowPositionParity =
(windowVOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2;
GLRenderer->mPresent3dRowShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentCheckerInterleaved()
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dCheckerShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
//auto clientoffset = screen->GetClientOffset();
//auto windowHOffset = clientoffset.X % 2;
//auto windowVOffset = clientoffset.Y % 2;
int windowHOffset = 0;
int windowVOffset = 0;
GLRenderer->mPresent3dCheckerShader->Uniforms->WindowPositionParity =
(windowVOffset
+ windowHOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2; // because we want the top pixel offset, but gl_FragCoord.y is the bottom pixel offset
GLRenderer->mPresent3dCheckerShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
//==========================================================================
//
// Sometimes the stereo render context is not ready immediately at start up
//
//==========================================================================
bool QuadStereoCheckInitialRenderContextState()
{
// Keep trying until we see at least one good OpenGL context to render to
static bool bQuadStereoSupported = false;
static bool bDecentContextWasFound = false;
static int contextCheckCount = 0;
if ((!bDecentContextWasFound) && (contextCheckCount < 200))
{
contextCheckCount += 1;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // This question is about the main screen display context
GLboolean supportsStereo, supportsBuffered;
glGetBooleanv(GL_DOUBLEBUFFER, &supportsBuffered);
if (supportsBuffered) // Finally, a useful OpenGL context
{
// This block will be executed exactly ONCE during a game run
bDecentContextWasFound = true; // now we can stop checking every frame...
// Now check whether this context supports hardware stereo
glGetBooleanv(GL_STEREO, &supportsStereo);
bQuadStereoSupported = supportsStereo && supportsBuffered;
}
}
return bQuadStereoSupported;
}
//==========================================================================
//
//
//
//==========================================================================
static void PresentQuadStereo()
{
if (QuadStereoCheckInitialRenderContextState())
{
GLRenderer->mBuffers->BindOutputFB();
glDrawBuffer(GL_BACK_LEFT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK_RIGHT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK);
}
else
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
}
void FGLRenderer::PresentStereo()
{
switch (vr_mode)
{
default:
return;
case VR_GREENMAGENTA:
PresentAnaglyph(false, true, true);
break;
case VR_REDCYAN:
PresentAnaglyph(true, false, false);
break;
case VR_AMBERBLUE:
PresentAnaglyph(true, true, false);
break;
case VR_SIDEBYSIDEFULL:
case VR_SIDEBYSIDESQUISHED:
PresentSideBySide();
break;
case VR_TOPBOTTOM:
PresentTopBottom();
break;
case VR_ROWINTERLEAVED:
PresentRowInterleaved();
break;
case VR_COLUMNINTERLEAVED:
PresentColumnInterleaved();
break;
case VR_CHECKERINTERLEAVED:
PresentCheckerInterleaved();
break;
case VR_QUADSTEREO:
PresentQuadStereo();
break;
}
}

View file

@ -40,7 +40,6 @@
#include "hwrenderer/scene/hw_clipper.h"
#include "gl/scene/gl_portal.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/stereo3d/scoped_color_mask.h"
#include "gl/renderer/gl_quaddrawer.h"
#include "gl/dynlights/gl_lightbuffer.h"
@ -253,26 +252,25 @@ void FDrawInfo::SetupFloodStencil(wallseg * ws)
// Create stencil
glStencilFunc(GL_EQUAL, recursion, ~0); // create stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels
{
// Use revertible color mask, to avoid stomping on anaglyph 3D state
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
gl_RenderState.EnableTexture(false);
gl_RenderState.ResetColor();
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
gl_RenderState.EnableTexture(false);
gl_RenderState.ResetColor();
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
gl_RenderState.Apply();
FQuadDrawer qd;
qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0);
qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0);
qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0);
qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0);
qd.Render(GL_TRIANGLE_FAN);
gl_RenderState.Apply();
FQuadDrawer qd;
qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0);
qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0);
qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0);
qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0);
qd.Render(GL_TRIANGLE_FAN);
glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
} // glColorMask(1, 1, 1, 1); // don't write to the graphics buffer
glColorMask(1, 1, 1, 1); // don't write to the graphics buffer
gl_RenderState.EnableTexture(true);
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
@ -282,26 +280,25 @@ void FDrawInfo::ClearFloodStencil(wallseg * ws)
{
int recursion = GLRenderer->mPortalState.GetRecursion();
glStencilOp(GL_KEEP,GL_KEEP,GL_DECR);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
gl_RenderState.EnableTexture(false);
{
// Use revertible color mask, to avoid stomping on anaglyph 3D state
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // don't write to the graphics buffer
gl_RenderState.ResetColor();
// Use revertible color mask, to avoid stomping on anaglyph 3D state
glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
gl_RenderState.ResetColor();
gl_RenderState.Apply();
FQuadDrawer qd;
qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0);
qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0);
qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0);
qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0);
qd.Render(GL_TRIANGLE_FAN);
gl_RenderState.Apply();
FQuadDrawer qd;
qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0);
qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0);
qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0);
qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0);
qd.Render(GL_TRIANGLE_FAN);
// restore old stencil op.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, recursion, ~0);
gl_RenderState.EnableTexture(true);
} // glColorMask(1, 1, 1, 1);
// restore old stencil op.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, recursion, ~0);
gl_RenderState.EnableTexture(true);
glColorMask(1, 1, 1, 1);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}

View file

@ -42,7 +42,6 @@
#include "gl/data/gl_vertexbuffer.h"
#include "hwrenderer/scene/hw_clipper.h"
#include "gl/scene/gl_portal.h"
#include "gl/stereo3d/scoped_color_mask.h"
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
@ -135,7 +134,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // create stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels
{
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // don't write to the graphics buffer
glColorMask(0,0,0,0); // don't write to the graphics buffer
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.ResetColor();
@ -166,7 +165,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw
// set normal drawing mode
gl_RenderState.EnableTexture(true);
glDepthFunc(GL_LESS);
// glColorMask(1, 1, 1, 1);
glColorMask(1, 1, 1, 1);
gl_RenderState.SetEffect(EFF_NONE);
glDepthRange(0, 1);
@ -194,7 +193,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw
glStencilFunc(GL_EQUAL, mState->recursion + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
gl_RenderState.EnableTexture(true);
// glColorMask(1,1,1,1);
glColorMask(1,1,1,1);
gl_RenderState.SetEffect(EFF_NONE);
glDisable(GL_DEPTH_TEST);
glDepthMask(false); // don't write to Z-buffer!
@ -242,46 +241,45 @@ void GLPortal::End(HWDrawInfo *di, bool usestencil)
// Restore the old view
if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility;
glColorMask(0, 0, 0, 0); // no graphics
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.ResetColor();
gl_RenderState.EnableTexture(false);
gl_RenderState.Apply();
if (needdepth)
{
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.ResetColor();
gl_RenderState.EnableTexture(false);
gl_RenderState.Apply();
if (needdepth)
{
// first step: reset the depth buffer to max. depth
glDepthRange(1, 1); // always
glDepthFunc(GL_ALWAYS); // write the farthest depth value
DrawPortalStencil();
}
else
{
glEnable(GL_DEPTH_TEST);
}
// second step: restore the depth buffer to the previous values and reset the stencil
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil
// first step: reset the depth buffer to max. depth
glDepthRange(1, 1); // always
glDepthFunc(GL_ALWAYS); // write the farthest depth value
DrawPortalStencil();
glDepthFunc(GL_LESS);
}
else
{
glEnable(GL_DEPTH_TEST);
}
// second step: restore the depth buffer to the previous values and reset the stencil
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil
DrawPortalStencil();
glDepthFunc(GL_LESS);
gl_RenderState.EnableTexture(true);
gl_RenderState.SetEffect(EFF_NONE);
} // glColorMask(1, 1, 1, 1);
gl_RenderState.EnableTexture(true);
gl_RenderState.SetEffect(EFF_NONE);
glColorMask(1, 1, 1, 1);
mState->recursion--;
// restore old stencil op.
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
glStencilFunc(GL_EQUAL, mState->recursion,~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil
}
else
{
if (needdepth)
if (needdepth)
{
glClear(GL_DEPTH_BUFFER_BIT);
}
@ -302,17 +300,17 @@ void GLPortal::End(HWDrawInfo *di, bool usestencil)
gl_RenderState.ResetColor();
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
{
ScopedColorMask colorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.BlendFunc(GL_ONE, GL_ZERO);
gl_RenderState.BlendEquation(GL_FUNC_ADD);
gl_RenderState.Apply();
DrawPortalStencil();
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.EnableTexture(true);
}
glColorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.BlendFunc(GL_ONE, GL_ZERO);
gl_RenderState.BlendEquation(GL_FUNC_ADD);
gl_RenderState.Apply();
DrawPortalStencil();
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.EnableTexture(true);
glColorMask(1, 1, 1, 1); // mark portal in alpha channel but don't touch color
glDepthFunc(GL_LESS);
}
}

View file

@ -53,8 +53,7 @@
#include "hwrenderer/scene/hw_portal.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_portal.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "hwrenderer/utility/scoped_view_shifter.h"
#include "hwrenderer/utility/hw_vrmodes.h"
//==========================================================================
//
@ -380,10 +379,10 @@ void FDrawInfo::EndDrawScene(sector_t * viewsector)
void FDrawInfo::DrawEndScene2D(sector_t * viewsector)
{
const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player);
auto vrmode = VRMode::GetVRMode(true);
// This should be removed once all 2D stuff is really done through the 2D interface.
VPUniforms.mViewMatrix.loadIdentity();
VPUniforms.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f);
VPUniforms.mProjectionMatrix = vrmode->GetHUDSpriteProjection();
ApplyVPUniforms();
glDisable(GL_DEPTH_TEST);
glDisable(GL_MULTISAMPLE);
@ -465,27 +464,24 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came
R_SetupFrame (mainvp, r_viewwindow, camera);
// Render (potentially) multiple views for stereo 3d
float viewShift[3];
const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode();
stereo3dMode.SetUp();
for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix)
// Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode.
auto vrmode = VRMode::GetVRMode(mainview && toscreen);
for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix)
{
const auto eye = stereo3dMode.getEyePose(eye_ix);
eye->SetUp();
const auto &eye = vrmode->mEyes[eye_ix];
screen->SetViewportRects(bounds);
Set3DViewport(mainview);
FDrawInfo *di = FDrawInfo::StartDrawInfo(mainvp, nullptr);
auto vp = di->Viewpoint;
auto &vp = di->Viewpoint;
di->SetViewArea();
auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr);
di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
// Stereo mode specific perspective projection
di->VPUniforms.mProjectionMatrix = eye->GetProjection(fov, ratio, fovratio);
// Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos
eye->GetViewShift(vp.HWAngles.Yaw.Degrees, viewShift);
ScopedViewShifter viewShifter(vp.Pos, viewShift);
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio);
// Stereo mode specific viewpoint adjustment
vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees);
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false);
@ -497,7 +493,7 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came
PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector); });
}
di->EndDrawInfo();
if (!stereo3dMode.IsMono())
if (vrmode->mEyeCount > 1)
mBuffers->BlitToEyeTexture(eye_ix);
}

View file

@ -38,7 +38,6 @@
#include "gl/data/gl_vertexbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/models/gl_models.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/dynlights/gl_lightbuffer.h"
//==========================================================================
@ -91,8 +90,6 @@ void FDrawInfo::DrawPSprite (HUDSprite *huds)
void FDrawInfo::DrawPlayerSprites(bool hudModelStep)
{
s3d::Stereo3DMode::getCurrentMode().AdjustPlayerSprites(this);
int oldlightmode = level.lightmode;
if (!hudModelStep && level.lightmode == 8) level.lightmode = 2; // Software lighting cannot handle 2D content so revert to lightmode 2 for that.
for(auto &hudsprite : hudsprites)

View file

@ -1,84 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_anaglyph.cpp
** Color mask based stereoscopic 3D modes for GZDoom
**
*/
#include "gl_anaglyph.h"
#include "gl/renderer/gl_renderbuffers.h"
namespace s3d {
MaskAnaglyph::MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters)
: leftEye(leftColorMask, ipdMeters), rightEye(leftColorMask.inverse(), ipdMeters)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
void MaskAnaglyph::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
gl_RenderState.SetColorMask(leftEye.GetColorMask().r, leftEye.GetColorMask().g, leftEye.GetColorMask().b, true);
gl_RenderState.ApplyColorMask();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
gl_RenderState.SetColorMask(rightEye.GetColorMask().r, rightEye.GetColorMask().g, rightEye.GetColorMask().b, true);
gl_RenderState.ApplyColorMask();
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
gl_RenderState.ResetColorMask();
gl_RenderState.ApplyColorMask();
}
/* static */
const GreenMagenta& GreenMagenta::getInstance(float ipd)
{
static GreenMagenta instance(ipd);
return instance;
}
/* static */
const RedCyan& RedCyan::getInstance(float ipd)
{
static RedCyan instance(ipd);
return instance;
}
/* static */
const AmberBlue& AmberBlue::getInstance(float ipd)
{
static AmberBlue instance(ipd);
return instance;
}
} /* namespace s3d */

View file

@ -1,113 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_anaglyph.h
** Color mask based stereoscopic 3D modes for GZDoom
**
*/
#ifndef GL_ANAGLYPH_H_
#define GL_ANAGLYPH_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
namespace s3d {
class ColorMask
{
public:
ColorMask(bool r, bool g, bool b) : r(r), g(g), b(b) {}
ColorMask inverse() const { return ColorMask(!r, !g, !b); }
bool r;
bool g;
bool b;
};
class AnaglyphLeftPose : public LeftEyePose
{
public:
AnaglyphLeftPose(const ColorMask& colorMask, float ipd) : LeftEyePose(ipd), colorMask(colorMask) {}
ColorMask GetColorMask() const { return colorMask; }
private:
ColorMask colorMask;
};
class AnaglyphRightPose : public RightEyePose
{
public:
AnaglyphRightPose(const ColorMask& colorMask, float ipd) : RightEyePose(ipd), colorMask(colorMask) {}
ColorMask GetColorMask() const { return colorMask; }
private:
ColorMask colorMask;
};
class MaskAnaglyph : public Stereo3DMode
{
public:
MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters);
void Present() const override;
private:
AnaglyphLeftPose leftEye;
AnaglyphRightPose rightEye;
};
class RedCyan : public MaskAnaglyph
{
public:
static const RedCyan& getInstance(float ipd);
RedCyan(float ipd) : MaskAnaglyph(ColorMask(true, false, false), ipd) {}
};
class GreenMagenta : public MaskAnaglyph
{
public:
static const GreenMagenta& getInstance(float ipd);
GreenMagenta(float ipd) : MaskAnaglyph(ColorMask(false, true, false), ipd) {}
};
class AmberBlue : public MaskAnaglyph
{
public:
static const AmberBlue& getInstance(float ipd);
AmberBlue(float ipd) : MaskAnaglyph(ColorMask(true, true, false), ipd) {}
};
// TODO matrix anaglyph
} /* namespace s3d */
#endif /* GL_ANAGLYPH_H_ */

View file

@ -1,232 +0,0 @@
/*
** gl_interleaved3d.cpp
** Interleaved image stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/
#include "gl_interleaved3d.h"
#include "gl/renderer/gl_renderbuffers.h"
#include "gl/renderer/gl_postprocessstate.h"
#include "gl/system/gl_framebuffer.h"
#include "hwrenderer/postprocessing/hw_present3dRowshader.h"
#ifdef _WIN32
#include "hardware.h"
#endif // _WIN32
EXTERN_CVAR(Float, vid_saturation)
EXTERN_CVAR(Float, vid_brightness)
EXTERN_CVAR(Float, vid_contrast)
EXTERN_CVAR(Int, gl_satformula)
EXTERN_CVAR(Bool, fullscreen)
EXTERN_CVAR(Int, win_x) // screen pixel position of left of display window
EXTERN_CVAR(Int, win_y) // screen pixel position of top of display window
namespace s3d {
/* static */
const CheckerInterleaved3D& CheckerInterleaved3D::getInstance(float ipd)
{
static CheckerInterleaved3D instance(ipd);
return instance;
}
/* static */
const ColumnInterleaved3D& ColumnInterleaved3D::getInstance(float ipd)
{
static ColumnInterleaved3D instance(ipd);
return instance;
}
/* static */
const RowInterleaved3D& RowInterleaved3D::getInstance(float ipd)
{
static RowInterleaved3D instance(ipd);
return instance;
}
static void prepareInterleavedPresent(FPresentShaderBase& shader)
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Bind each eye texture, for composition in the shader
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->mBuffers->BindEyeTexture(1, 1);
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
const IntRect& box = screen->mOutputLetterbox;
glViewport(box.left, box.top, box.width, box.height);
shader.Bind(NOQUEUE);
if ( GLRenderer->framebuffer->IsHWGammaActive() )
{
shader.Uniforms->InvGamma = 1.0f;
shader.Uniforms->Contrast = 1.0f;
shader.Uniforms->Brightness = 0.0f;
shader.Uniforms->Saturation = 1.0f;
}
else
{
shader.Uniforms->InvGamma = 1.0f / clamp<float>(Gamma, 0.1f, 4.f);
shader.Uniforms->Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
shader.Uniforms->Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
shader.Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.0f);
shader.Uniforms->GrayFormula = static_cast<int>(gl_satformula);
}
shader.Uniforms->Scale = {
screen->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(),
screen->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight()
};
shader.Uniforms.Set();
}
// fixme: I don't know how to get absolute window position on Mac and Linux
// fixme: I don't know how to get window border decoration size anywhere
// So for now I'll hard code the border effect on my test machine.
// Workaround for others is to fuss with vr_swap_eyes CVAR until it looks right.
// Presumably the top/left window border on my test machine has an odd number of pixels
// in the horizontal direction, and an even number in the vertical direction.
#define WINDOW_BORDER_HORIZONTAL_PARITY 1
#define WINDOW_BORDER_VERTICAL_PARITY 0
void CheckerInterleaved3D::Present() const
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dCheckerShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
int windowVOffset = 0;
int windowHOffset = 0;
#ifdef _WIN32
/* this needs to be done differently!
if (!fullscreen) {
I_SaveWindowedPos(); // update win_y CVAR
windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2;
windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2;
}
*/
#endif // _WIN32
GLRenderer->mPresent3dCheckerShader->Uniforms->WindowPositionParity =
(windowVOffset
+ windowHOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2; // because we want the top pixel offset, but gl_FragCoord.y is the bottom pixel offset
GLRenderer->mPresent3dCheckerShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
void s3d::CheckerInterleaved3D::AdjustViewports() const
{
// decrease the total pixel count by 2, but keep the same aspect ratio
const float sqrt2 = 1.41421356237f;
// Change size of renderbuffer, and align to screen
screen->mSceneViewport.height /= sqrt2;
screen->mSceneViewport.top /= sqrt2;
screen->mSceneViewport.width /= sqrt2;
screen->mSceneViewport.left /= sqrt2;
screen->mScreenViewport.height /= sqrt2;
screen->mScreenViewport.top /= sqrt2;
screen->mScreenViewport.width /= sqrt2;
screen->mScreenViewport.left /= sqrt2;
}
void ColumnInterleaved3D::Present() const
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dColumnShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
int windowHOffset = 0;
#ifdef _WIN32
/* this needs to be done differently!
if (!fullscreen) {
I_SaveWindowedPos(); // update win_y CVAR
windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2;
}
*/
#endif // _WIN32
GLRenderer->mPresent3dColumnShader->Uniforms->WindowPositionParity = windowHOffset;
GLRenderer->mPresent3dColumnShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
void RowInterleaved3D::Present() const
{
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
prepareInterleavedPresent(*GLRenderer->mPresent3dRowShader);
// Compute absolute offset from top of screen to top of current display window
// because we need screen-relative, not window-relative, scan line parity
int windowVOffset = 0;
#ifdef _WIN32
/* this needs to be done differently!
if (! fullscreen) {
I_SaveWindowedPos(); // update win_y CVAR
windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2;
}
*/
#endif // _WIN32
GLRenderer->mPresent3dRowShader->Uniforms->WindowPositionParity =
(windowVOffset
+ screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom
) % 2;
GLRenderer->mPresent3dRowShader->Uniforms.Set();
GLRenderer->RenderScreenQuad();
}
} /* namespace s3d */

View file

@ -1,75 +0,0 @@
/*
** gl_interleaved3d.h
** Interleaved stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/
#ifndef GL_INTERLEAVED3D_H_
#define GL_INTERLEAVED3D_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_sidebyside3d.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
namespace s3d {
class CheckerInterleaved3D : public SideBySideSquished
{
public:
static const CheckerInterleaved3D& getInstance(float ipd);
CheckerInterleaved3D(double ipdMeters) : SideBySideSquished(ipdMeters) {}
void Present() const override;
void AdjustViewports() const override;
};
class ColumnInterleaved3D : public SideBySideSquished
{
public:
static const ColumnInterleaved3D& getInstance(float ipd);
ColumnInterleaved3D(double ipdMeters) : SideBySideSquished(ipdMeters) {}
void Present() const override;
};
class RowInterleaved3D : public TopBottom3D
{
public:
static const RowInterleaved3D& getInstance(float ipd);
RowInterleaved3D(double ipdMeters) : TopBottom3D(ipdMeters) {}
void Present() const override;
};
} /* namespace s3d */
#endif /* GL_INTERLEAVED3D_H_ */

View file

@ -1,115 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_quadstereo.cpp
** Quad-buffered OpenGL stereoscopic 3D mode for GZDoom
**
*/
#include "gl_quadstereo.h"
#include "gl/renderer/gl_renderbuffers.h"
namespace s3d {
QuadStereo::QuadStereo(double ipdMeters)
: leftEye(ipdMeters), rightEye(ipdMeters)
{
// Check whether quad-buffered stereo is supported in the current context
// We are assuming the OpenGL context is already current at this point,
// i.e. this constructor is called "just in time".
// First initialize to mono-ish initial state
bQuadStereoSupported = leftEye.bQuadStereoSupported = rightEye.bQuadStereoSupported = false;
eye_ptrs.Push(&leftEye); // We ALWAYS want to show at least this one view...
// We will possibly advance to true stereo mode in the Setup() method...
}
// Sometimes the stereo render context is not ready immediately at start up
/* private */
void QuadStereo::checkInitialRenderContextState()
{
// Keep trying until we see at least one good OpenGL context to render to
static bool bDecentContextWasFound = false;
static int contextCheckCount = 0;
if ( (! bDecentContextWasFound) && (contextCheckCount < 200) )
{
contextCheckCount += 1;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // This question is about the main screen display context
GLboolean supportsStereo, supportsBuffered;
glGetBooleanv(GL_DOUBLEBUFFER, &supportsBuffered);
if (supportsBuffered) // Finally, a useful OpenGL context
{
// This block will be executed exactly ONCE during a game run
bDecentContextWasFound = true; // now we can stop checking every frame...
// Now check whether this context supports hardware stereo
glGetBooleanv(GL_STEREO, &supportsStereo);
bQuadStereoSupported = supportsStereo && supportsBuffered;
leftEye.bQuadStereoSupported = bQuadStereoSupported;
rightEye.bQuadStereoSupported = bQuadStereoSupported;
if (bQuadStereoSupported)
eye_ptrs.Push(&rightEye); // Use the other eye too, if we can do stereo
}
}
}
void QuadStereo::Present() const
{
if (bQuadStereoSupported)
{
GLRenderer->mBuffers->BindOutputFB();
glDrawBuffer(GL_BACK_LEFT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK_RIGHT);
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
glDrawBuffer(GL_BACK);
}
else
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
}
void QuadStereo::SetUp() const
{
Stereo3DMode::SetUp();
// Maybe advance to true stereo mode (ONCE), after the stereo context is finally ready
const_cast<QuadStereo*>(this)->checkInitialRenderContextState();
}
/* static */
const QuadStereo& QuadStereo::getInstance(float ipd)
{
static QuadStereo instance(ipd);
return instance;
}
} /* namespace s3d */

View file

@ -1,77 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_quadstereo.h
** Quad-buffered OpenGL stereoscopic 3D mode for GZDoom
**
*/
#ifndef GL_QUADSTEREO_H_
#define GL_QUADSTEREO_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_load/gl_system.h"
namespace s3d {
class QuadStereoLeftPose : public LeftEyePose
{
public:
QuadStereoLeftPose(float ipd) : LeftEyePose(ipd), bQuadStereoSupported(false) {}
bool bQuadStereoSupported;
};
class QuadStereoRightPose : public RightEyePose
{
public:
QuadStereoRightPose(float ipd) : RightEyePose(ipd), bQuadStereoSupported(false){}
bool bQuadStereoSupported;
};
// To use Quad-buffered stereo mode with nvidia 3d vision glasses,
// you must either:
// A) be using a Quadro series video card, OR
//
// B) be using nvidia driver version 314.07 or later
// AND have your monitor set to 120 Hz refresh rate
// AND have gzdoom in true full screen mode
class QuadStereo : public Stereo3DMode
{
public:
QuadStereo(double ipdMeters);
void Present() const override;
void SetUp() const override;
static const QuadStereo& getInstance(float ipd);
private:
QuadStereoLeftPose leftEye;
QuadStereoRightPose rightEye;
bool bQuadStereoSupported;
void checkInitialRenderContextState();
};
} /* namespace s3d */
#endif /* GL_QUADSTEREO_H_ */

View file

@ -1,150 +0,0 @@
/*
** gl_sidebyside3d.cpp
** Mosaic image stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/
#include "gl/scene/gl_drawinfo.h"
#include "gl_sidebyside3d.h"
#include "gl/renderer/gl_renderbuffers.h"
namespace s3d {
void SideBySideBase::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int leftWidth = screen->mOutputLetterbox.width / 2;
int rightWidth = screen->mOutputLetterbox.width - leftWidth;
IntRect leftHalfScreen = screen->mOutputLetterbox;
leftHalfScreen.width = leftWidth;
IntRect rightHalfScreen = screen->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
screen->mSceneViewport.width /= 2;
screen->mSceneViewport.left /= 2;
screen->mScreenViewport.width /= 2;
screen->mScreenViewport.left /= 2;
}
/* static */
const SideBySideSquished& SideBySideSquished::getInstance(float ipd)
{
static SideBySideSquished instance(ipd);
return instance;
}
SideBySideSquished::SideBySideSquished(double ipdMeters)
: leftEye(ipdMeters), rightEye(ipdMeters)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
/* static */
const SideBySideFull& SideBySideFull::getInstance(float ipd)
{
static SideBySideFull instance(ipd);
return instance;
}
SideBySideFull::SideBySideFull(double ipdMeters)
: leftEye(ipdMeters, 0.5f), rightEye(ipdMeters, 0.5f)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
/* virtual */
void SideBySideFull::AdjustPlayerSprites(FDrawInfo *di) const /* override */
{
// Show weapon at double width, so it would appear normal width after rescaling
int w = screen->mScreenViewport.width;
int h = screen->mScreenViewport.height;
di->VPUniforms.mProjectionMatrix.ortho(w/2, w + w/2, h, 0, -1.0f, 1.0f);
di->ApplyVPUniforms();
}
/* static */
const TopBottom3D& TopBottom3D::getInstance(float ipd)
{
static TopBottom3D instance(ipd);
return instance;
}
void TopBottom3D::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
// Compute screen regions to use for left and right eye views
int topHeight = screen->mOutputLetterbox.height / 2;
int bottomHeight = screen->mOutputLetterbox.height - topHeight;
IntRect topHalfScreen = screen->mOutputLetterbox;
topHalfScreen.height = topHeight;
topHalfScreen.top = topHeight;
IntRect bottomHalfScreen = screen->mOutputLetterbox;
bottomHalfScreen.height = bottomHeight;
bottomHalfScreen.top = 0;
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(topHalfScreen, true);
GLRenderer->mBuffers->BindEyeTexture(1, 0);
GLRenderer->DrawPresentTexture(bottomHalfScreen, true);
}
// AdjustViewports() is called from within FLGRenderer::SetOutputViewport(...)
void TopBottom3D::AdjustViewports() const
{
// Change size of renderbuffer, and align to screen
screen->mSceneViewport.height /= 2;
screen->mSceneViewport.top /= 2;
screen->mScreenViewport.height /= 2;
screen->mScreenViewport.top /= 2;
}
} /* namespace s3d */

View file

@ -1,87 +0,0 @@
/*
** gl_sidebyside3d.h
** Mosaic image stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2016 Christopher Bruns
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/
#ifndef GL_SIDEBYSIDE3D_H_
#define GL_SIDEBYSIDE3D_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
namespace s3d {
class SideBySideBase : public Stereo3DMode
{
public:
void Present() const override;
virtual void AdjustViewports() const override;
};
class SideBySideSquished : public SideBySideBase
{
public:
static const SideBySideSquished& getInstance(float ipd);
SideBySideSquished(double ipdMeters);
private:
LeftEyePose leftEye;
RightEyePose rightEye;
};
class SideBySideFull : public SideBySideBase
{
public:
static const SideBySideFull& getInstance(float ipd);
SideBySideFull(double ipdMeters);
virtual void AdjustPlayerSprites(FDrawInfo *di) const override;
private:
LeftEyePose leftEye;
RightEyePose rightEye;
};
class TopBottom3D : public SideBySideSquished
{
public:
static const TopBottom3D& getInstance(float ipd);
TopBottom3D(double ipdMeters) : SideBySideSquished(ipdMeters) {}
void Present() const override;
virtual void AdjustViewports() const override;
};
} /* namespace s3d */
#endif /* GL_SIDEBYSIDE3D_H_ */

View file

@ -1,53 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo3d.cpp
** Stereoscopic 3D API
**
*/
#include "gl_load/gl_system.h"
#include "gl/stereo3d/gl_stereo3d.h"
namespace s3d {
Stereo3DMode::Stereo3DMode()
{
}
Stereo3DMode::~Stereo3DMode()
{
}
// Avoid static initialization order fiasco by declaring first Mode type (Mono) here in the
// same source file as Stereo3DMode::getCurrentMode()
// https://isocpp.org/wiki/faq/ctors#static-init-order
/* static */
const MonoView& MonoView::getInstance()
{
static MonoView instance;
return instance;
}
} /* namespace s3d */

View file

@ -1,90 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo3d.h
** Stereoscopic 3D API
**
*/
#ifndef GL_STEREO3D_H_
#define GL_STEREO3D_H_
#include <cstring> // needed for memcpy on linux, which is needed by VSMatrix copy ctor
#include "tarray.h"
#include "r_data/matrix.h"
#include "gl/renderer/gl_renderer.h"
#include "hwrenderer/stereo3d/hw_eyepose.h"
/* stereoscopic 3D API */
namespace s3d {
/* Base class for stereoscopic 3D rendering modes */
class Stereo3DMode
{
public:
/* static methods for managing the selected stereoscopic view state */
static const Stereo3DMode& getCurrentMode();
static const Stereo3DMode& getMonoMode();
Stereo3DMode();
virtual ~Stereo3DMode();
virtual int eye_count() const { return eye_ptrs.Size(); }
virtual const EyePose * getEyePose(int ix) const { return eye_ptrs(ix); }
/* hooks for setup and cleanup operations for each stereo mode */
virtual void SetUp() const {};
virtual bool IsMono() const { return false; }
virtual void AdjustViewports() const {};
virtual void AdjustPlayerSprites(FDrawInfo *di) const {};
virtual void Present() const = 0;
protected:
TArray<const EyePose *> eye_ptrs;
private:
static Stereo3DMode const * currentStereo3DMode;
static void setCurrentMode(const Stereo3DMode& mode);
};
/**
* Ordinary non-3D rendering
*/
class MonoView : public Stereo3DMode
{
public:
static const MonoView& getInstance();
bool IsMono() const override { return true; }
void Present() const override { }
protected:
MonoView() { eye_ptrs.Push(&centralEye); }
EyePose centralEye;
};
} /* namespace st3d */
#endif /* GL_STEREO3D_H_ */

View file

@ -1,127 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_cvars.cpp
** Console variables related to stereoscopic 3D in GZDoom
**
*/
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/stereo3d/gl_stereo_leftright.h"
#include "gl/stereo3d/gl_anaglyph.h"
#include "gl/stereo3d/gl_quadstereo.h"
#include "gl/stereo3d/gl_sidebyside3d.h"
#include "gl/stereo3d/gl_interleaved3d.h"
#include "version.h"
// Set up 3D-specific console variables:
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
// switch left and right eye views
CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG)
// intraocular distance in meters
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS
// distance between viewer and the display screen
CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// default conversion between (vertical) DOOM units and meters
CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// Manage changing of 3D modes:
namespace s3d {
// Initialize static member
Stereo3DMode const * Stereo3DMode::currentStereo3DMode = 0; // "nullptr" not resolved on linux (presumably not C++11)
/* static */
void Stereo3DMode::setCurrentMode(const Stereo3DMode& mode) {
Stereo3DMode::currentStereo3DMode = &mode;
}
/* static */
const Stereo3DMode& Stereo3DMode::getCurrentMode()
{
// NOTE: Ensure that these vr_mode values correspond to the ones in wadsrc/static/menudef.z
switch (vr_mode)
{
case 1:
setCurrentMode(GreenMagenta::getInstance(vr_ipd));
break;
case 2:
setCurrentMode(RedCyan::getInstance(vr_ipd));
break;
case 3:
setCurrentMode(SideBySideFull::getInstance(vr_ipd));
break;
case 4:
setCurrentMode(SideBySideSquished::getInstance(vr_ipd));
break;
case 5:
setCurrentMode(LeftEyeView::getInstance(vr_ipd));
break;
case 6:
setCurrentMode(RightEyeView::getInstance(vr_ipd));
break;
case 7:
if (screen->enable_quadbuffered) {
setCurrentMode(QuadStereo::getInstance(vr_ipd));
}
else {
setCurrentMode(MonoView::getInstance());
}
break;
// TODO: 8: Oculus Rift
case 9:
setCurrentMode(AmberBlue::getInstance(vr_ipd));
break;
// TODO: 10: HTC Vive/OpenVR
case 11:
setCurrentMode(TopBottom3D::getInstance(vr_ipd));
break;
case 12:
setCurrentMode(RowInterleaved3D::getInstance(vr_ipd));
break;
case 13:
setCurrentMode(ColumnInterleaved3D::getInstance(vr_ipd));
break;
case 14:
setCurrentMode(CheckerInterleaved3D::getInstance(vr_ipd));
break;
case 0:
default:
setCurrentMode(MonoView::getInstance());
break;
}
return *currentStereo3DMode;
}
const Stereo3DMode& Stereo3DMode::getMonoMode()
{
setCurrentMode(MonoView::getInstance());
return *currentStereo3DMode;
}
} /* namespace s3d */

View file

@ -1,73 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_leftright.cpp
** Offsets for left and right eye views
**
*/
#include "gl_stereo_leftright.h"
#include "vectors.h" // RAD2DEG
#include "doomtype.h" // M_PI
#include "hwrenderer/utility/hw_cvars.h"
#include "gl_load/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderbuffers.h"
namespace s3d {
/* static */
const LeftEyeView& LeftEyeView::getInstance(float ipd)
{
static LeftEyeView instance(ipd);
instance.setIpd(ipd);
return instance;
}
void LeftEyeView::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
/* static */
const RightEyeView& RightEyeView::getInstance(float ipd)
{
static RightEyeView instance(ipd);
instance.setIpd(ipd);
return instance;
}
void RightEyeView::Present() const
{
GLRenderer->mBuffers->BindOutputFB();
GLRenderer->ClearBorders();
GLRenderer->mBuffers->BindEyeTexture(0, 0);
GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true);
}
} /* namespace s3d */

View file

@ -1,67 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_leftright.h
** Offsets for left and right eye views
**
*/
#ifndef GL_STEREO_LEFTRIGHT_H_
#define GL_STEREO_LEFTRIGHT_H_
#include "gl_stereo3d.h"
namespace s3d {
/**
* As if viewed through the left eye only
*/
class LeftEyeView : public Stereo3DMode
{
public:
static const LeftEyeView& getInstance(float ipd);
LeftEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); }
void setIpd(float ipd) { eye.setIpd(ipd); }
void Present() const override;
protected:
LeftEyePose eye;
};
class RightEyeView : public Stereo3DMode
{
public:
static const RightEyeView& getInstance(float ipd);
RightEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); }
void setIpd(float ipd) { eye.setIpd(ipd); }
void Present() const override;
protected:
RightEyePose eye;
};
} /* namespace s3d */
#endif /* GL_STEREO_LEFTRIGHT_H_ */

View file

@ -1,56 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** scoped_color_mask.h
** Stack-scoped class for temporarily changing the OpenGL color mask setting.
**
*/
#ifndef GL_STEREO3D_SCOPED_COLOR_MASK_H_
#define GL_STEREO3D_SCOPED_COLOR_MASK_H_
#include "gl_load/gl_system.h"
/**
* Temporarily change color mask
*/
class ScopedColorMask
{
public:
ScopedColorMask(bool r, bool g, bool b, bool a)
{
gl_RenderState.GetColorMask(saved[0], saved[1], saved[2], saved[3]);
gl_RenderState.SetColorMask(r, g, b, a);
gl_RenderState.ApplyColorMask();
gl_RenderState.EnableDrawBuffers(1);
}
~ScopedColorMask() {
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
gl_RenderState.SetColorMask(saved[0], saved[1], saved[2], saved[3]);
gl_RenderState.ApplyColorMask();
}
private:
bool saved[4];
};
#endif // GL_STEREO3D_SCOPED_COLOR_MASK_H_

View file

@ -37,10 +37,10 @@
#include "gl/renderer/gl_renderbuffers.h"
#include "gl/textures/gl_samplers.h"
#include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/data/gl_uniformbuffer.h"
#include "gl/models/gl_models.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/shaders/gl_shaderprogram.h"
#include "gl_debug.h"
#include "r_videoscale.h"
@ -375,7 +375,10 @@ void OpenGLFrameBuffer::SetViewportRects(IntRect *bounds)
{
Super::SetViewportRects(bounds);
if (!bounds)
s3d::Stereo3DMode::getCurrentMode().AdjustViewports();
{
auto vrmode = VRMode::GetVRMode(true);
vrmode->AdjustViewport();
}
}

View file

@ -1,97 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_leftright.cpp
** Offsets for left and right eye views
**
*/
#include "vectors.h" // RAD2DEG
#include "doomtype.h" // M_PI
#include "hwrenderer/utility/hw_cvars.h"
#include "hw_eyepose.h"
#include "v_video.h"
EXTERN_CVAR(Float, vr_screendist)
EXTERN_CVAR(Float, vr_hunits_per_meter)
EXTERN_CVAR(Bool, vr_swap_eyes)
/* virtual */
VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const
{
VSMatrix result;
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar());
return result;
}
/* virtual */
void EyePose::GetViewShift(float yaw, float outViewShift[3]) const
{
// pass-through for Mono view
outViewShift[0] = 0;
outViewShift[1] = 0;
outViewShift[2] = 0;
}
/* virtual */
VSMatrix ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const
{
double zNear = screen->GetZNear();
double zFar = screen->GetZFar();
// For stereo 3D, use asymmetric frustum shift in projection matrix
// Q: shouldn't shift vary with roll angle, at least for desktop display?
// A: No. (lab) roll is not measured on desktop display (yet)
double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units
// double frustumShift = 0; // Turning off shift for debugging
double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio;
double fW = fH * aspectRatio * squish;
double left = -fW - frustumShift;
double right = fW - frustumShift;
double bottom = -fH;
double top = fH;
VSMatrix result(1);
result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar);
return result;
}
/* virtual */
void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const
{
float dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
float dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
outViewShift[0] = dx;
outViewShift[1] = dy;
outViewShift[2] = 0;
}
float ShiftedEyePose::getShift() const
{
return vr_swap_eyes ? -shift : shift;
}

View file

@ -1,47 +0,0 @@
#pragma once
#include "r_data/matrix.h"
/* Viewpoint of one eye */
class EyePose
{
public:
EyePose() {}
virtual ~EyePose() {}
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
virtual void GetViewShift(float yaw, float outViewShift[3]) const;
virtual void SetUp() const {}
};
class ShiftedEyePose : public EyePose
{
public:
ShiftedEyePose(float shift, float squish) : shift(shift), squish(squish) {};
float getShift() const;
virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
virtual void GetViewShift(float yaw, float outViewShift[3]) const;
protected:
void setShift(float shift) { this->shift = shift; }
private:
float shift;
float squish;
};
class LeftEyePose : public ShiftedEyePose
{
public:
LeftEyePose(float ipd, float squish = 1.f) : ShiftedEyePose( -0.5f * ipd, squish) {}
void setIpd(float ipd) { setShift(-0.5f * ipd); }
};
class RightEyePose : public ShiftedEyePose
{
public:
RightEyePose(float ipd, float squish = 1.f) : ShiftedEyePose(0.5f * ipd, squish) {}
void setIpd(float ipd) { setShift(0.5f * ipd); }
};

View file

@ -0,0 +1,176 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_stereo_leftright.cpp
** Offsets for left and right eye views
**
*/
#include "vectors.h" // RAD2DEG
#include "doomtype.h" // M_PI
#include "hwrenderer/utility/hw_cvars.h"
#include "hw_vrmodes.h"
#include "v_video.h"
// Set up 3D-specific console variables:
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
// switch left and right eye views
CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG)
// intraocular distance in meters
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS
// distance between viewer and the display screen
CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// default conversion between (vertical) DOOM units and meters
CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
#define isqrt2 0.7071067812f
static VRMode vrmi_mono = { 1, 1.f, 1.f, 1.f,{ { 0.f, 1.f },{ 0.f, 0.f } } };
static VRMode vrmi_stereo = { 2, 1.f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
static VRMode vrmi_sbsfull = { 2, .5f, 1.f, 2.f,{ { -.5f, .5f },{ .5f, .5f } } };
static VRMode vrmi_sbssquished = { 2, .5f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
static VRMode vrmi_lefteye = { 1, 1.f, 1.f, 1.f, { { -.5f, 1.f },{ 0.f, 0.f } } };
static VRMode vrmi_righteye = { 1, 1.f, 1.f, 1.f,{ { .5f, 1.f },{ 0.f, 0.f } } };
static VRMode vrmi_topbottom = { 2, 1.f, .5f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
const VRMode *VRMode::GetVRMode(bool toscreen)
{
switch (toscreen && vid_rendermode == 4 ? vr_mode : 0)
{
default:
case VR_MONO:
return &vrmi_mono;
case VR_GREENMAGENTA:
case VR_REDCYAN:
case VR_QUADSTEREO:
case VR_AMBERBLUE:
return &vrmi_stereo;
case VR_SIDEBYSIDESQUISHED:
case VR_COLUMNINTERLEAVED:
return &vrmi_sbssquished;
case VR_SIDEBYSIDEFULL:
return &vrmi_sbsfull;
case VR_TOPBOTTOM:
case VR_ROWINTERLEAVED:
return &vrmi_topbottom;
case VR_LEFTEYEVIEW:
return &vrmi_lefteye;
case VR_RIGHTEYEVIEW:
return &vrmi_righteye;
case VR_CHECKERINTERLEAVED:
return &vrmi_checker;
}
}
void VRMode::AdjustViewport() const
{
screen->mSceneViewport.height = (int)(screen->mSceneViewport.height * mVerticalViewportScale);
screen->mSceneViewport.top = (int)(screen->mSceneViewport.top * mVerticalViewportScale);
screen->mSceneViewport.width = (int)(screen->mSceneViewport.width * mHorizontalViewportScale);
screen->mSceneViewport.left = (int)(screen->mSceneViewport.left * mHorizontalViewportScale);
screen->mScreenViewport.height = (int)(screen->mScreenViewport.height * mVerticalViewportScale);
screen->mScreenViewport.top = (int)(screen->mScreenViewport.top * mVerticalViewportScale);
screen->mScreenViewport.width = (int)(screen->mScreenViewport.width * mHorizontalViewportScale);
screen->mScreenViewport.left = (int)(screen->mScreenViewport.left * mHorizontalViewportScale);
}
VSMatrix VRMode::GetHUDSpriteProjection() const
{
VSMatrix mat;
int w = screen->mScreenViewport.width;
int h = screen->mScreenViewport.height;
float scaled_w = w * mWeaponProjectionScale;
float left_ofs = (scaled_w - w) / 2.f;
mat.ortho(left_ofs, left_ofs + w, (float)h, 0, -1.0f, 1.0f);
return mat;
}
float VREyeInfo::getShift() const
{
auto res = mShiftFactor * vr_ipd;
return vr_swap_eyes ? -res : res;
}
VSMatrix VREyeInfo::GetProjection(float fov, float aspectRatio, float fovRatio) const
{
VSMatrix result;
if (mShiftFactor == 0)
{
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar());
return result;
}
else
{
double zNear = screen->GetZNear();
double zFar = screen->GetZFar();
// For stereo 3D, use asymmetric frustum shift in projection matrix
// Q: shouldn't shift vary with roll angle, at least for desktop display?
// A: No. (lab) roll is not measured on desktop display (yet)
double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units
// double frustumShift = 0; // Turning off shift for debugging
double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio;
double fW = fH * aspectRatio * mScaleFactor;
double left = -fW - frustumShift;
double right = fW - frustumShift;
double bottom = -fH;
double top = fH;
VSMatrix result(1);
result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar);
return result;
}
}
/* virtual */
DVector3 VREyeInfo::GetViewShift(float yaw) const
{
if (mShiftFactor == 0)
{
// pass-through for Mono view
return { 0,0,0 };
}
else
{
double dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
double dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
return { dx, dy, 0 };
}
}

View file

@ -0,0 +1,46 @@
#pragma once
#include "r_data/matrix.h"
enum
{
VR_MONO = 0,
VR_GREENMAGENTA = 1,
VR_REDCYAN = 2,
VR_SIDEBYSIDEFULL = 3,
VR_SIDEBYSIDESQUISHED = 4,
VR_LEFTEYEVIEW = 5,
VR_RIGHTEYEVIEW = 6,
VR_QUADSTEREO = 7,
VR_AMBERBLUE = 9,
VR_TOPBOTTOM = 11,
VR_ROWINTERLEAVED = 12,
VR_COLUMNINTERLEAVED = 13,
VR_CHECKERINTERLEAVED = 14
};
struct VREyeInfo
{
float mShiftFactor;
float mScaleFactor;
VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
DVector3 GetViewShift(float yaw) const;
private:
float getShift() const;
};
struct VRMode
{
int mEyeCount;
float mHorizontalViewportScale;
float mVerticalViewportScale;
float mWeaponProjectionScale;
VREyeInfo mEyes[2];
static const VRMode *GetVRMode(bool toscreen = true);
void AdjustViewport() const;
VSMatrix GetHUDSpriteProjection() const;
};

View file

@ -1,62 +0,0 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2015 Christopher Bruns
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** scoped_view_shifter.h
** Stack-scoped class for temporarily changing camera viewpoint
** Used for stereoscopic 3D.
**
*/
#ifndef GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
#define GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
#include "basictypes.h"
#include "vectors.h"
/**
* Temporarily shift
*/
class ScopedViewShifter
{
public:
ScopedViewShifter(DVector3 &var, float dxyz[3]) // in meters
{
// save original values
mVar = &var;
cachedView = var;
// modify values
var += DVector3(dxyz[0], dxyz[1], dxyz[2]);
}
~ScopedViewShifter()
{
// restore original values
*mVar = cachedView;
}
private:
DVector3 *mVar;
DVector3 cachedView;
};
#endif // GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_