mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-11 23:32:04 +00:00
Copy stereo3d source files from my GLOOME fork.
This commit is contained in:
parent
4e1723eeb7
commit
694dff67e4
10 changed files with 548 additions and 0 deletions
29
src/gl/stereo3d/gl_anaglyph.cpp
Normal file
29
src/gl/stereo3d/gl_anaglyph.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "gl_anaglyph.h"
|
||||
|
||||
namespace s3d {
|
||||
|
||||
MaskAnaglyph::MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters)
|
||||
: leftEye(leftColorMask, ipdMeters), rightEye(leftColorMask.inverse(), ipdMeters)
|
||||
{
|
||||
eye_ptrs.push_back(&leftEye);
|
||||
eye_ptrs.push_back(&rightEye);
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
} /* namespace s3d */
|
75
src/gl/stereo3d/gl_anaglyph.h
Normal file
75
src/gl/stereo3d/gl_anaglyph.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef GL_ANAGLYPH_H_
|
||||
#define GL_ANAGLYPH_H_
|
||||
|
||||
#include "gl_stereo3d.h"
|
||||
#include "gl_stereo_leftright.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); }
|
||||
|
||||
GLboolean r;
|
||||
GLboolean g;
|
||||
GLboolean b;
|
||||
};
|
||||
|
||||
|
||||
class AnaglyphLeftPose : public LeftEyePose
|
||||
{
|
||||
public:
|
||||
AnaglyphLeftPose(const ColorMask& colorMask, float ipd) : LeftEyePose(ipd), colorMask(colorMask) {}
|
||||
virtual void SetUp() const { glColorMask(colorMask.r, colorMask.g, colorMask.b, true); }
|
||||
virtual void TearDown() const { glColorMask(1,1,1,1); }
|
||||
private:
|
||||
ColorMask colorMask;
|
||||
};
|
||||
|
||||
class AnaglyphRightPose : public RightEyePose
|
||||
{
|
||||
public:
|
||||
AnaglyphRightPose(const ColorMask& colorMask, float ipd) : RightEyePose(ipd), colorMask(colorMask) {}
|
||||
virtual void SetUp() const { glColorMask(colorMask.r, colorMask.g, colorMask.b, true); }
|
||||
virtual void TearDown() const { glColorMask(1,1,1,1); }
|
||||
private:
|
||||
ColorMask colorMask;
|
||||
};
|
||||
|
||||
class MaskAnaglyph : public Stereo3DMode
|
||||
{
|
||||
public:
|
||||
MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters);
|
||||
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) {}
|
||||
};
|
||||
|
||||
// TODO matrix anaglyph
|
||||
|
||||
|
||||
} /* namespace st3d */
|
||||
|
||||
|
||||
#endif /* GL_ANAGLYPH_H_ */
|
71
src/gl/stereo3d/gl_stereo3d.cpp
Normal file
71
src/gl/stereo3d/gl_stereo3d.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "gl/stereo3d/gl_stereo3d.h"
|
||||
#include "vectors.h" // RAD2DEG
|
||||
#include "doomtype.h" // M_PI
|
||||
|
||||
namespace s3d {
|
||||
|
||||
|
||||
/* virtual */
|
||||
void EyePose::GetProjection(float fov, float aspectRatio, float fovRatio, GLdouble m[4][4]) const
|
||||
{
|
||||
// Lifted from gl_scene.cpp FGLRenderer::SetProjection()
|
||||
float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio));
|
||||
const double zNear = 5.0;
|
||||
const double zFar = 65536.0;
|
||||
|
||||
double sine, cotangent, deltaZ;
|
||||
double radians = fovy / 2 * M_PI / 180;
|
||||
|
||||
deltaZ = zFar - zNear;
|
||||
sine = sin(radians);
|
||||
if ((deltaZ == 0) || (sine == 0) || (aspectRatio == 0)) {
|
||||
return;
|
||||
}
|
||||
cotangent = cos(radians) / sine;
|
||||
|
||||
memset(m, 0, 16*sizeof(GLdouble));
|
||||
m[0][0] = cotangent / aspectRatio;
|
||||
m[1][1] = cotangent;
|
||||
m[2][2] = -(zFar + zNear) / deltaZ;
|
||||
m[2][3] = -1;
|
||||
m[3][2] = -2 * zNear * zFar / deltaZ;
|
||||
m[3][3] = 0;
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
Viewport EyePose::GetViewport(const Viewport& fullViewport) const
|
||||
{
|
||||
return fullViewport;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */
|
||||
void EyePose::GetViewShift(float yaw, float outViewShift[3]) const
|
||||
{
|
||||
// pass-through for Mono view
|
||||
outViewShift[0] = 0;
|
||||
outViewShift[1] = 0;
|
||||
outViewShift[2] = 0;
|
||||
}
|
||||
|
||||
|
||||
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 */
|
80
src/gl/stereo3d/gl_stereo3d.h
Normal file
80
src/gl/stereo3d/gl_stereo3d.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
#ifndef GL_STEREO3D_H_
|
||||
#define GL_STEREO3D_H_
|
||||
|
||||
#include <vector>
|
||||
#include "gl/system/gl_system.h"
|
||||
|
||||
|
||||
/* stereoscopic 3D API */
|
||||
namespace s3d {
|
||||
|
||||
|
||||
/* Subregion of current display window */
|
||||
class Viewport
|
||||
{
|
||||
public:
|
||||
int x, y;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
|
||||
/* Viewpoint of one eye */
|
||||
class EyePose
|
||||
{
|
||||
public:
|
||||
EyePose() {}
|
||||
virtual ~EyePose() {}
|
||||
virtual void GetProjection(float fov, float aspectRatio, float fovRatio, GLdouble outMatrix[4][4]) const;
|
||||
virtual Viewport GetViewport(const Viewport& fullViewport) const;
|
||||
virtual void GetViewShift(float yaw, float outViewShift[3]) const;
|
||||
virtual void SetUp() const {};
|
||||
virtual void TearDown() const {};
|
||||
};
|
||||
|
||||
|
||||
/* Base class for stereoscopic 3D rendering modes */
|
||||
class Stereo3DMode
|
||||
{
|
||||
public:
|
||||
/* const_iterator cycles through the various eye viewpoints */
|
||||
typedef std::vector<const EyePose *>::const_iterator const_iterator;
|
||||
|
||||
/* static methods for managing the selected stereoscopic view state */
|
||||
static const Stereo3DMode& getCurrentMode();
|
||||
|
||||
Stereo3DMode();
|
||||
virtual ~Stereo3DMode();
|
||||
/* const_iterator cycles through the various eye viewpoints */
|
||||
virtual const_iterator begin() const { return eye_ptrs.begin(); }
|
||||
virtual const_iterator end() const { return eye_ptrs.end(); }
|
||||
/* hooks for setup and cleanup operations for each stereo mode */
|
||||
virtual void SetUp() const {};
|
||||
virtual void TearDown() const {};
|
||||
|
||||
protected:
|
||||
std::vector<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();
|
||||
|
||||
protected:
|
||||
MonoView() { eye_ptrs.push_back(¢ralEye); }
|
||||
EyePose centralEye;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace st3d */
|
||||
|
||||
|
||||
#endif /* GL_STEREO3D_H_ */
|
51
src/gl/stereo3d/gl_stereo_cvars.cpp
Normal file
51
src/gl/stereo3d/gl_stereo_cvars.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "gl/stereo3d/gl_stereo3d.h"
|
||||
#include "gl/stereo3d/gl_stereo_leftright.h"
|
||||
#include "gl/stereo3d/gl_anaglyph.h"
|
||||
#include "gl/system/gl_cvars.h"
|
||||
|
||||
// Set up 3D-specific console variables:
|
||||
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
|
||||
// intraocular distance in meters
|
||||
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS
|
||||
CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // 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 = nullptr;
|
||||
|
||||
/* 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(LeftEyeView::getInstance(vr_ipd));
|
||||
break;
|
||||
case 4:
|
||||
setCurrentMode(RightEyeView::getInstance(vr_ipd));
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
setCurrentMode(MonoView::getInstance());
|
||||
break;
|
||||
}
|
||||
return *currentStereo3DMode;
|
||||
}
|
||||
|
||||
} /* namespace s3d */
|
||||
|
79
src/gl/stereo3d/gl_stereo_leftright.cpp
Normal file
79
src/gl/stereo3d/gl_stereo_leftright.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "gl_stereo_leftright.h"
|
||||
#include "vectors.h" // RAD2DEG
|
||||
#include "doomtype.h" // M_PI
|
||||
#include "gl/system/gl_cvars.h"
|
||||
#include <cmath>
|
||||
|
||||
EXTERN_CVAR(Float, vr_screendist)
|
||||
EXTERN_CVAR(Float, vr_hunits_per_meter)
|
||||
|
||||
namespace s3d {
|
||||
|
||||
|
||||
/* virtual */
|
||||
void ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio, GLdouble m[4][4]) const
|
||||
{
|
||||
// Lifted from gl_scene.cpp FGLRenderer::SetProjection()
|
||||
float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio));
|
||||
double zNear = 5.0;
|
||||
double zFar = 65536.0;
|
||||
|
||||
// For stereo 3D, use asymmetric frustum shift in projection matrix
|
||||
// Q: shouldn't shift vary with roll angle, at least for desktop display?
|
||||
// A: (lab) roll is not measured on desktop display (yet)
|
||||
double frustumShift = zNear * shift / vr_screendist; // meters cancel
|
||||
// double frustumShift = 0; // Turning off shift for debugging
|
||||
double fH = tan(fovy / 360 * M_PI) * zNear;
|
||||
double fW = fH * aspectRatio;
|
||||
// Emulate glFrustum command:
|
||||
// glFrustum(-fW - frustumShift, fW - frustumShift, -fH, fH, zNear, zFar);
|
||||
double left = -fW - frustumShift;
|
||||
double right = fW - frustumShift;
|
||||
double bottom = -fH;
|
||||
double top = fH;
|
||||
double deltaZ = zFar - zNear;
|
||||
|
||||
memset(m, 0, 16 * sizeof(GLdouble)); // set all elements to zero, cleverly
|
||||
|
||||
// https://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml
|
||||
m[0][0] = 2 * zNear / (right - left);
|
||||
m[1][1] = 2 * zNear / (top - bottom);
|
||||
m[2][2] = -(zFar + zNear) / deltaZ;
|
||||
m[2][3] = -1;
|
||||
m[3][2] = -2 * zNear * zFar / deltaZ;
|
||||
// m[3][3] = 0; // redundant
|
||||
// m[2][1] = (top + bottom) / (top - bottom); // zero for the cases I know of...
|
||||
m[2][0] = (right + left) / (right - left); // asymmetric shift is in this term
|
||||
}
|
||||
|
||||
|
||||
/* virtual */
|
||||
void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const
|
||||
{
|
||||
float dx = cos(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
|
||||
float dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
|
||||
outViewShift[0] = dx;
|
||||
outViewShift[1] = dy;
|
||||
outViewShift[2] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* static */
|
||||
const LeftEyeView& LeftEyeView::getInstance(float ipd)
|
||||
{
|
||||
static LeftEyeView instance(ipd);
|
||||
instance.setIpd(ipd);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
/* static */
|
||||
const RightEyeView& RightEyeView::getInstance(float ipd)
|
||||
{
|
||||
static RightEyeView instance(ipd);
|
||||
instance.setIpd(ipd);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
} /* namespace s3d */
|
71
src/gl/stereo3d/gl_stereo_leftright.h
Normal file
71
src/gl/stereo3d/gl_stereo_leftright.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
#ifndef GL_STEREO_LEFTRIGHT_H_
|
||||
#define GL_STEREO_LEFTRIGHT_H_
|
||||
|
||||
#include "gl_stereo3d.h"
|
||||
|
||||
namespace s3d {
|
||||
|
||||
|
||||
class ShiftedEyePose : public EyePose
|
||||
{
|
||||
public:
|
||||
ShiftedEyePose(float shift) : shift(shift) {};
|
||||
float getShift() const { return shift; }
|
||||
void setShift(float shift) { this->shift = shift; }
|
||||
virtual void GetProjection(float fov, float aspectRatio, float fovRatio, GLdouble outMatrix[4][4]) const;
|
||||
virtual void GetViewShift(float yaw, float outViewShift[3]) const;
|
||||
protected:
|
||||
float shift;
|
||||
};
|
||||
|
||||
|
||||
class LeftEyePose : public ShiftedEyePose
|
||||
{
|
||||
public:
|
||||
LeftEyePose(float ipd) : ShiftedEyePose(-0.5*ipd) {}
|
||||
float getIpd() const { return -2.0*getShift(); }
|
||||
void setIpd(float ipd) { setShift(-0.5*ipd); }
|
||||
};
|
||||
|
||||
|
||||
class RightEyePose : public ShiftedEyePose
|
||||
{
|
||||
public:
|
||||
RightEyePose(float ipd) : ShiftedEyePose(+0.5*ipd) {}
|
||||
float getIpd() const { return +2.0*shift; }
|
||||
void setIpd(float ipd) { setShift(+0.5*ipd); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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_back(&eye); }
|
||||
float getIpd() const { return eye.getIpd(); }
|
||||
void setIpd(float ipd) { eye.setIpd(ipd); }
|
||||
protected:
|
||||
LeftEyePose eye;
|
||||
};
|
||||
|
||||
|
||||
class RightEyeView : public Stereo3DMode
|
||||
{
|
||||
public:
|
||||
static const RightEyeView& getInstance(float ipd);
|
||||
|
||||
RightEyeView(float ipd) : eye(ipd) { eye_ptrs.push_back(&eye); }
|
||||
float getIpd() const { return eye.getIpd(); }
|
||||
void setIpd(float ipd) { eye.setIpd(ipd); }
|
||||
protected:
|
||||
RightEyePose eye;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace s3d */
|
||||
|
||||
#endif /* GL_STEREO_LEFTRIGHT_H_ */
|
38
src/gl/stereo3d/scoped_color_mask.h
Normal file
38
src/gl/stereo3d/scoped_color_mask.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef GL_STEREO3D_SCOPED_COLOR_MASK_H_
|
||||
#define GL_STEREO3D_SCOPED_COLOR_MASK_H_
|
||||
|
||||
#include "gl/glew.h"
|
||||
|
||||
/**
|
||||
* Temporarily change color mask
|
||||
*/
|
||||
class ScopedColorMask
|
||||
{
|
||||
public:
|
||||
ScopedColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a)
|
||||
: isPushed(false)
|
||||
{
|
||||
setColorMask(r, g, b, a);
|
||||
}
|
||||
~ScopedColorMask() {
|
||||
revert();
|
||||
}
|
||||
void setColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
|
||||
if (!isPushed) {
|
||||
glPushAttrib(GL_COLOR_BUFFER_BIT);
|
||||
isPushed = true;
|
||||
}
|
||||
glColorMask(r, g, b, a);
|
||||
}
|
||||
void revert() {
|
||||
if (isPushed) {
|
||||
glPopAttrib();
|
||||
isPushed = false;
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool isPushed;
|
||||
};
|
||||
|
||||
|
||||
#endif // GL_STEREO3D_SCOPED_COLOR_MASK_H_
|
29
src/gl/stereo3d/scoped_view_shifter.cpp
Normal file
29
src/gl/stereo3d/scoped_view_shifter.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "scoped_view_shifter.h"
|
||||
#include "r_utility.h"
|
||||
|
||||
namespace s3d {
|
||||
|
||||
ScopedViewShifter::ScopedViewShifter(float dxyz[3]) // in meters
|
||||
{
|
||||
// save original values
|
||||
cachedViewx = viewx;
|
||||
cachedViewy = viewy;
|
||||
cachedViewz = viewz;
|
||||
// modify values
|
||||
float fViewx = FIXED2FLOAT(viewx) - dxyz[0];
|
||||
float fViewy = FIXED2FLOAT(viewy) + dxyz[1];
|
||||
float fViewz = FIXED2FLOAT(viewz) + dxyz[2];
|
||||
viewx = FLOAT2FIXED(fViewx);
|
||||
viewy = FLOAT2FIXED(fViewy);
|
||||
viewz = FLOAT2FIXED(fViewz);
|
||||
}
|
||||
|
||||
ScopedViewShifter::~ScopedViewShifter()
|
||||
{
|
||||
// restore original values
|
||||
viewx = cachedViewx;
|
||||
viewy = cachedViewy;
|
||||
viewz = cachedViewz;
|
||||
}
|
||||
|
||||
}
|
25
src/gl/stereo3d/scoped_view_shifter.h
Normal file
25
src/gl/stereo3d/scoped_view_shifter.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
|
||||
#define GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
|
||||
|
||||
#include "basictypes.h"
|
||||
|
||||
namespace s3d {
|
||||
|
||||
/**
|
||||
* Temporarily shift viewx, viewy, viewz
|
||||
*/
|
||||
class ScopedViewShifter
|
||||
{
|
||||
public:
|
||||
ScopedViewShifter(float dxyz[3]); // in meters
|
||||
~ScopedViewShifter();
|
||||
|
||||
private:
|
||||
fixed_t cachedViewx;
|
||||
fixed_t cachedViewy;
|
||||
fixed_t cachedViewz;
|
||||
};
|
||||
|
||||
} /* namespace s3d */
|
||||
|
||||
#endif // GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
|
Loading…
Reference in a new issue