Initial implementation of five 3D modes -- some bugs remain.

This commit is contained in:
Christopher Bruns 2015-10-30 20:51:35 -04:00
parent 694dff67e4
commit 0874455faf
16 changed files with 219 additions and 172 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
*.cpp text

1
.gitignore vendored
View file

@ -40,3 +40,4 @@
/zlib/x64/
/build_vc2013_64bit
/build_vc2015
/build_cmake

View file

@ -762,6 +762,7 @@ 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
@ -1120,6 +1121,11 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE
gl/scene/gl_walls_draw.cpp
gl/scene/gl_vertex.cpp
gl/scene/gl_spritelight.cpp
gl/stereo3d/gl_stereo3d.cpp
gl/stereo3d/gl_stereo_cvars.cpp
gl/stereo3d/gl_stereo_leftright.cpp
gl/stereo3d/scoped_view_shifter.cpp
gl/stereo3d/gl_anaglyph.cpp
gl/dynlights/gl_dynlight.cpp
gl/dynlights/gl_glow.cpp
gl/dynlights/gl_dynlight1.cpp
@ -1371,6 +1377,7 @@ source_group("OpenGL Renderer\\HQ Resize Assembly version" REGULAR_EXPRESSION "^
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

@ -5,6 +5,7 @@
#include "v_video.h"
#include "vectors.h"
#include "r_renderer.h"
#include "gl/data/gl_matrix.h"
struct particle_t;
class FCanvasTexture;
@ -120,6 +121,7 @@ public:
void Flush() {}
void SetProjection(float fov, float ratio, float fovratio);
void SetProjection(FLOATTYPE matrix[4][4]); // raw matrix input from stereo 3d modes
void SetViewMatrix(fixed_t viewx, fixed_t viewy, fixed_t viewz, bool mirror, bool planemirror);
void ProcessScene(bool toscreen = false);

View file

@ -56,6 +56,7 @@
#include "gl/utility/gl_clock.h"
#include "gl/utility/gl_templates.h"
#include "gl/shaders/gl_shader.h"
#include "gl/stereo3d/scoped_color_mask.h"
FDrawInfo * gl_drawinfo;
@ -1011,7 +1012,9 @@ 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
glColorMask(0,0,0,0); // don't write to the graphics buffer
{
// 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);
@ -1033,7 +1036,7 @@ void FDrawInfo::SetupFloodStencil(wallseg * ws)
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);
@ -1045,7 +1048,9 @@ void FDrawInfo::ClearFloodStencil(wallseg * ws)
glStencilOp(GL_KEEP,GL_KEEP,GL_DECR);
gl_RenderState.EnableTexture(false);
glColorMask(0,0,0,0); // don't write to the graphics buffer
{
// 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();
gl_RenderState.Apply();
@ -1064,7 +1069,7 @@ void FDrawInfo::ClearFloodStencil(wallseg * ws)
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, recursion, ~0);
gl_RenderState.EnableTexture(true);
glColorMask(1,1,1,1);
} // glColorMask(1, 1, 1, 1);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}

View file

@ -59,6 +59,7 @@
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_portal.h"
#include "gl/shaders/gl_shader.h"
#include "gl/stereo3d/scoped_color_mask.h"
#include "gl/textures/gl_material.h"
#include "gl/utility/gl_clock.h"
#include "gl/utility/gl_templates.h"
@ -197,7 +198,7 @@ bool GLPortal::Start(bool usestencil, bool doquery)
// Create stencil
glStencilFunc(GL_EQUAL,recursion,~0); // create stencil
glStencilOp(GL_KEEP,GL_KEEP,GL_INCR); // increment stencil of valid pixels
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();
@ -206,6 +207,8 @@ bool GLPortal::Start(bool usestencil, bool doquery)
if (NeedDepthBuffer())
{
{
ScopedColorMask colorMask(0, 0, 0, 0);
glDepthMask(false); // don't write to Z-buffer!
if (!NeedDepthBuffer()) doquery = false; // too much overhead and nothing to gain.
else if (gl_noquery) doquery = false;
@ -233,7 +236,7 @@ bool GLPortal::Start(bool usestencil, bool doquery)
// 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);
@ -258,12 +261,13 @@ bool GLPortal::Start(bool usestencil, bool doquery)
// than the benefit.
// Note: We must draw the stencil with z-write enabled here because there is no second pass!
ScopedColorMask colorMask(0, 0, 0, 0);
glDepthMask(true);
DrawPortalStencil();
glStencilFunc(GL_EQUAL,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!
@ -371,7 +375,8 @@ void GLPortal::End(bool usestencil)
in_area=savedviewarea;
GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1));
glColorMask(0,0,0,0); // no graphics
{
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);
@ -400,7 +405,7 @@ void GLPortal::End(bool usestencil)
gl_RenderState.EnableTexture(true);
gl_RenderState.SetEffect(EFF_NONE);
glColorMask(1, 1, 1, 1);
} // glColorMask(1, 1, 1, 1);
recursion--;
// restore old stencil op.
@ -434,13 +439,14 @@ void GLPortal::End(bool usestencil)
gl_RenderState.ResetColor();
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
glColorMask(0,0,0,0); // no graphics
{
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // no graphics
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
DrawPortalStencil();
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.EnableTexture(true);
glColorMask(1,1,1,1);
} // glColorMask(1, 1, 1, 1);
glDepthFunc(GL_LESS);
}
PortalAll.Unclock();

View file

@ -71,6 +71,8 @@
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_portal.h"
#include "gl/shaders/gl_shader.h"
#include "gl/stereo3d/gl_stereo3d.h"
#include "gl/stereo3d/scoped_view_shifter.h"
#include "gl/textures/gl_material.h"
#include "gl/utility/gl_clock.h"
#include "gl/utility/gl_convert.h"
@ -254,6 +256,13 @@ void FGLRenderer::SetProjection(float fov, float ratio, float fovratio)
gl_RenderState.Set2DMode(false);
}
// raw matrix input from stereo 3d modes
void FGLRenderer::SetProjection(FLOATTYPE matrix[4][4])
{
gl_RenderState.mProjectionMatrix.loadMatrix(&matrix[0][0]);
gl_RenderState.Set2DMode(false);
}
//-----------------------------------------------------------------------------
//
// Setup the modelview matrix
@ -802,10 +811,26 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo
retval = viewsector;
// Render (potentially) multiple views for stereo 3d
FLOATTYPE projectionMatrix[4][4];
float viewShift[3];
const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode();
stereo3dMode.SetUp();
s3d::Stereo3DMode::const_iterator eye;
for (eye = stereo3dMode.begin(); eye != stereo3dMode.end(); ++eye)
{
(*eye)->SetUp();
// TODO: stereo specific viewport - needed when implementing side-by-side modes etc.
SetViewport(bounds);
mCurrentFoV = fov;
// Stereo mode specific perspective projection
(*eye)->GetProjection(fov, ratio, fovratio, projectionMatrix);
SetProjection(projectionMatrix);
SetProjection(fov, ratio, fovratio); // switch to perspective mode and set up clipper
SetViewAngle(viewangle);
// Stereo mode specific viewpoint adjustment - temporarily shifts global viewx, viewy, viewz
(*eye)->GetViewShift(GLRenderer->mAngles.Yaw, viewShift);
s3d::ScopedViewShifter viewShifter(viewShift);
SetViewMatrix(viewx, viewy, viewz, false, false);
gl_RenderState.ApplyMatrices();
@ -814,6 +839,8 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo
clipper.SafeAddClipRangeRealAngles(viewangle + a1, viewangle - a1);
ProcessScene(toscreen);
}
stereo3dMode.TearDown();
gl_frameCount++; // This counter must be increased right before the interpolations are restored.
interpolator.RestoreInterpolations ();

View file

@ -11,7 +11,7 @@ MaskAnaglyph::MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters)
/* static */
const GreenMagenta& GreenMagenta::getInstance(float ipd)
const GreenMagenta& GreenMagenta::getInstance(FLOATTYPE ipd)
{
static GreenMagenta instance(ipd);
return instance;
@ -19,7 +19,7 @@ const GreenMagenta& GreenMagenta::getInstance(float ipd)
/* static */
const RedCyan& RedCyan::getInstance(float ipd)
const RedCyan& RedCyan::getInstance(FLOATTYPE ipd)
{
static RedCyan instance(ipd);
return instance;

View file

@ -3,6 +3,7 @@
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl/system/gl_system.h"
namespace s3d {

View file

@ -6,24 +6,23 @@ namespace s3d {
/* virtual */
void EyePose::GetProjection(float fov, float aspectRatio, float fovRatio, GLdouble m[4][4]) const
void EyePose::GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio, FLOATTYPE 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 fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio));
const FLOATTYPE zNear = 5.0;
const FLOATTYPE zFar = 65536.0;
double sine, cotangent, deltaZ;
double radians = fovy / 2 * M_PI / 180;
deltaZ = zFar - zNear;
sine = sin(radians);
FLOATTYPE deltaZ = zFar - zNear;
double sine = sin(radians);
if ((deltaZ == 0) || (sine == 0) || (aspectRatio == 0)) {
return;
}
cotangent = cos(radians) / sine;
FLOATTYPE cotangent = FLOATTYPE(cos(radians) / sine);
memset(m, 0, 16*sizeof(GLdouble));
memset(m, 0, 16*sizeof(FLOATTYPE));
m[0][0] = cotangent / aspectRatio;
m[1][1] = cotangent;
m[2][2] = -(zFar + zNear) / deltaZ;
@ -40,7 +39,7 @@ Viewport EyePose::GetViewport(const Viewport& fullViewport) const
/* virtual */
void EyePose::GetViewShift(float yaw, float outViewShift[3]) const
void EyePose::GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const
{
// pass-through for Mono view
outViewShift[0] = 0;

View file

@ -2,7 +2,7 @@
#define GL_STEREO3D_H_
#include <vector>
#include "gl/system/gl_system.h"
#include "gl/data/gl_matrix.h"
/* stereoscopic 3D API */
@ -24,9 +24,9 @@ class EyePose
public:
EyePose() {}
virtual ~EyePose() {}
virtual void GetProjection(float fov, float aspectRatio, float fovRatio, GLdouble outMatrix[4][4]) const;
virtual void GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio, FLOATTYPE outMatrix[4][4]) const;
virtual Viewport GetViewport(const Viewport& fullViewport) const;
virtual void GetViewShift(float yaw, float outViewShift[3]) const;
virtual void GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const;
virtual void SetUp() const {};
virtual void TearDown() const {};
};

View file

@ -33,10 +33,11 @@ const Stereo3DMode& Stereo3DMode::getCurrentMode()
case 2:
setCurrentMode(RedCyan::getInstance(vr_ipd));
break;
case 3:
// TODO: missing indices 3, 4 for not-yet-implemented side-by-side modes, to match values from GZ3Doom
case 5:
setCurrentMode(LeftEyeView::getInstance(vr_ipd));
break;
case 4:
case 6:
setCurrentMode(RightEyeView::getInstance(vr_ipd));
break;
case 0:

View file

@ -11,10 +11,10 @@ namespace s3d {
/* virtual */
void ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio, GLdouble m[4][4]) const
void ShiftedEyePose::GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio, FLOATTYPE m[4][4]) const
{
// Lifted from gl_scene.cpp FGLRenderer::SetProjection()
float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio));
FLOATTYPE fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio));
double zNear = 5.0;
double zFar = 65536.0;
@ -33,7 +33,7 @@ void ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio,
double top = fH;
double deltaZ = zFar - zNear;
memset(m, 0, 16 * sizeof(GLdouble)); // set all elements to zero, cleverly
memset(m, 0, 16 * sizeof(FLOATTYPE)); // set all elements to zero, cleverly
// https://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml
m[0][0] = 2 * zNear / (right - left);
@ -48,10 +48,10 @@ void ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio,
/* virtual */
void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const
void ShiftedEyePose::GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const
{
float dx = cos(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
float dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
FLOATTYPE dx = cos(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
FLOATTYPE dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
outViewShift[0] = dx;
outViewShift[1] = dy;
outViewShift[2] = 0;
@ -59,7 +59,7 @@ void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const
/* static */
const LeftEyeView& LeftEyeView::getInstance(float ipd)
const LeftEyeView& LeftEyeView::getInstance(FLOATTYPE ipd)
{
static LeftEyeView instance(ipd);
instance.setIpd(ipd);
@ -68,7 +68,7 @@ const LeftEyeView& LeftEyeView::getInstance(float ipd)
/* static */
const RightEyeView& RightEyeView::getInstance(float ipd)
const RightEyeView& RightEyeView::getInstance(FLOATTYPE ipd)
{
static RightEyeView instance(ipd);
instance.setIpd(ipd);

View file

@ -9,31 +9,31 @@ 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;
ShiftedEyePose(FLOATTYPE shift) : shift(shift) {};
FLOATTYPE getShift() const { return shift; }
void setShift(FLOATTYPE shift) { this->shift = shift; }
virtual void GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio, FLOATTYPE outMatrix[4][4]) const;
virtual void GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const;
protected:
float shift;
FLOATTYPE 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); }
LeftEyePose(FLOATTYPE ipd) : ShiftedEyePose( FLOATTYPE(-0.5) * ipd) {}
FLOATTYPE getIpd() const { return FLOATTYPE(-2.0)*getShift(); }
void setIpd(FLOATTYPE ipd) { setShift(FLOATTYPE(-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); }
RightEyePose(FLOATTYPE ipd) : ShiftedEyePose(FLOATTYPE(+0.5)*ipd) {}
FLOATTYPE getIpd() const { return FLOATTYPE(+2.0)*shift; }
void setIpd(FLOATTYPE ipd) { setShift(FLOATTYPE(+0.5)*ipd); }
};
@ -43,11 +43,11 @@ public:
class LeftEyeView : public Stereo3DMode
{
public:
static const LeftEyeView& getInstance(float ipd);
static const LeftEyeView& getInstance(FLOATTYPE ipd);
LeftEyeView(float ipd) : eye(ipd) { eye_ptrs.push_back(&eye); }
float getIpd() const { return eye.getIpd(); }
void setIpd(float ipd) { eye.setIpd(ipd); }
LeftEyeView(FLOATTYPE ipd) : eye(ipd) { eye_ptrs.push_back(&eye); }
FLOATTYPE getIpd() const { return eye.getIpd(); }
void setIpd(FLOATTYPE ipd) { eye.setIpd(ipd); }
protected:
LeftEyePose eye;
};
@ -56,11 +56,11 @@ protected:
class RightEyeView : public Stereo3DMode
{
public:
static const RightEyeView& getInstance(float ipd);
static const RightEyeView& getInstance(FLOATTYPE ipd);
RightEyeView(float ipd) : eye(ipd) { eye_ptrs.push_back(&eye); }
float getIpd() const { return eye.getIpd(); }
void setIpd(float ipd) { eye.setIpd(ipd); }
RightEyeView(FLOATTYPE ipd) : eye(ipd) { eye_ptrs.push_back(&eye); }
FLOATTYPE getIpd() const { return eye.getIpd(); }
void setIpd(FLOATTYPE ipd) { eye.setIpd(ipd); }
protected:
RightEyePose eye;
};

View file

@ -1,7 +1,7 @@
#ifndef GL_STEREO3D_SCOPED_COLOR_MASK_H_
#define GL_STEREO3D_SCOPED_COLOR_MASK_H_
#include "gl/glew.h"
#include "gl/system/gl_system.h"
/**
* Temporarily change color mask
@ -10,28 +10,15 @@ 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;
}
glGetBooleanv(GL_COLOR_WRITEMASK, saved);
glColorMask(r, g, b, a);
}
void revert() {
if (isPushed) {
glPopAttrib();
isPushed = false;
}
~ScopedColorMask() {
glColorMask(saved[0], saved[1], saved[2], saved[3]);
}
private:
bool isPushed;
GLboolean saved[4];
};

View file

@ -124,6 +124,15 @@ OptionValue "FuzzStyle"
//5, "Jagged fuzz" I can't see any difference between this and 4 so it's disabled for now.
}
OptionValue VRMode
{
0, "Normal"
1, "Green/Magenta"
2, "Red/Cyan"
5, "Left Eye"
6, "Right Eye"
}
OptionMenu "GLTextureGLOptions"
{
Title "TEXTURE OPTIONS"
@ -173,5 +182,6 @@ OptionMenu "GLPrefOptions"
Option "Particle style", gl_particles_style, "Particles"
Slider "Ambient light level", gl_light_ambient, 1.0, 255.0, 5.0
Option "Rendering quality", gl_render_precise, "Precision"
Option "Stereo 3D VR", vr_mode, "VRMode"
}