/* ** hw_vrmodes.cpp ** Matrix handling for stereo 3D rendering ** **--------------------------------------------------------------------------- ** Copyright 2015 Christopher Bruns ** Copyright 2016-2021 Christoph Oelckers ** 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 "vectors.h" // RAD2DEG #include "hw_cvars.h" #include "hw_vrmodes.h" #include "v_video.h" #include "version.h" #include "i_interface.h" // Set up 3D-specific console variables: CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // switch left and right eye views CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) // 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) { int mode = !toscreen || (sysCallbacks.DisableTextureFilter && sysCallbacks.DisableTextureFilter()) ? 0 : vr_mode; switch (mode) { default: case VR_MONO: return &vrmi_mono; case VR_GREENMAGENTA: case VR_REDCYAN: case VR_QUADSTEREO: case VR_AMBERBLUE: case VR_SIDEBYSIDELETTERBOX: 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(DFrameBuffer *screen) 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->GetWidth(); int h = screen->GetHeight(); float scaled_w = w / mWeaponProjectionScale; float left_ofs = (w - scaled_w) / 2.f; mat.ortho(left_ofs, left_ofs + scaled_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 fmat(1); fmat.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar); return fmat; } } /* 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 }; } }