diff --git a/code/CMakeModules/FindLibVR.cmake b/code/CMakeModules/FindLibVR.cmake
new file mode 100644
index 000000000..eba9c347e
--- /dev/null
+++ b/code/CMakeModules/FindLibVR.cmake
@@ -0,0 +1,32 @@
+# - Locate LibVR library
+# This module defines
+# LIBVR_LIBRARIES, the libraries to link against
+# LIBVR_FOUND, if false, do not try to link to LIBVR
+# LIBVR_INCLUDE_DIR, where to find headers.
+
+IF(LIBVR_LIBRARIES AND LIBVR_INCLUDE_DIR)
+ # in cache already
+ SET(LIBVR_FIND_QUIETLY TRUE)
+ENDIF(LIBVR_LIBRARIES AND LIBVR_INCLUDE_DIR)
+
+FIND_PATH(LIBVR_INCLUDE_DIR hmd.h
+ PATH_SUFFIXES include/LibVR
+)
+
+FIND_LIBRARY(LIBVR_LIBRARY
+ NAMES vr
+ PATH_SUFFIXES lib
+ PATHS
+)
+
+IF(LIBVR_LIBRARY AND LIBVR_INCLUDE_DIR)
+ IF(NOT LIBVR_FIND_QUIETLY)
+ MESSAGE(STATUS "Found LibVR: ${LIBVR_LIBRARY}")
+ ENDIF(NOT LIBVR_FIND_QUIETLY)
+ SET(LIBVR_FOUND "YES")
+ SET(LIBVR_DEFINITIONS "-DHAVE_LIBVR")
+ELSE(LIBVR_LIBRARY AND LIBVR_INCLUDE_DIR)
+ IF(NOT LIBVR_FIND_QUIETLY)
+ MESSAGE(STATUS "Warning: Unable to find LibVR!")
+ ENDIF(NOT LIBVR_FIND_QUIETLY)
+ENDIF(LIBVR_LIBRARY AND LIBVR_INCLUDE_DIR)
diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake
index 1547e2260..2fa51eb3a 100644
--- a/code/CMakeModules/nel.cmake
+++ b/code/CMakeModules/nel.cmake
@@ -322,6 +322,7 @@ MACRO(NL_SETUP_NEL_DEFAULT_OPTIONS)
OPTION(WITH_NEL_TESTS "Build NeL Unit Tests" ON )
OPTION(WITH_LIBOVR "With LibOVR support" OFF)
+ OPTION(WITH_LIBVR "With LibVR support" OFF)
ENDMACRO(NL_SETUP_NEL_DEFAULT_OPTIONS)
MACRO(NL_SETUP_NELNS_DEFAULT_OPTIONS)
diff --git a/code/nel/CMakeLists.txt b/code/nel/CMakeLists.txt
index 2a392ddf9..53bf071e3 100644
--- a/code/nel/CMakeLists.txt
+++ b/code/nel/CMakeLists.txt
@@ -45,6 +45,10 @@ IF(WITH_LIBOVR)
FIND_PACKAGE(LibOVR)
ENDIF(WITH_LIBOVR)
+IF(WITH_LIBVR)
+ FIND_PACKAGE(LibVR)
+ENDIF(WITH_LIBVR)
+
IF(WITH_INSTALL_LIBRARIES)
IF(UNIX)
SET(prefix ${CMAKE_INSTALL_PREFIX})
diff --git a/code/nel/include/nel/3d/stereo_libvr.h b/code/nel/include/nel/3d/stereo_libvr.h
new file mode 100644
index 000000000..76d1966fe
--- /dev/null
+++ b/code/nel/include/nel/3d/stereo_libvr.h
@@ -0,0 +1,160 @@
+/**
+ * \file stereo_libvr.h
+ * \brief CStereoLibVR
+ * \date 2013-08-19 19:17MT
+ * \author Thibaut Girka (ThibG)
+ * CStereoLibVR
+ */
+
+/*
+ * Copyright (C) 2013 by authors
+ *
+ * This file is part of NL3D.
+ * NL3D is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * NL3D 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 Affero General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with NL3D. If not, see
+ * .
+ */
+
+#ifndef NL3D_STEREO_LIBVR_H
+#define NL3D_STEREO_LIBVR_H
+
+#ifdef HAVE_LIBVR
+
+#include
+
+// STL includes
+
+// NeL includes
+#include
+#include
+
+// Project includes
+#include
+#include
+#include
+#include
+
+namespace NL3D {
+
+class ITexture;
+class CTextureUser;
+class CStereoLibVRDevicePtr;
+class CStereoLibVRDeviceHandle;
+class CPixelProgram;
+
+#define NL_STEREO_MAX_USER_CAMERAS 8
+
+/**
+ * \brief CStereoOVR
+ * \date 2013-06-25 22:22GMT
+ * \author Jan Boon (Kaetemi)
+ * CStereoOVR
+ */
+class CStereoLibVR : public IStereoHMD
+{
+public:
+ CStereoLibVR(const CStereoLibVRDeviceHandle *handle);
+ virtual ~CStereoLibVR();
+
+ /// Sets driver and generates necessary render targets
+ virtual void setDriver(NL3D::UDriver *driver);
+
+ /// Gets the required screen resolution for this device
+ virtual bool getScreenResolution(uint &width, uint &height);
+ /// Set latest camera position etcetera
+ virtual void updateCamera(uint cid, const NL3D::UCamera *camera);
+ /// Get the frustum to use for clipping
+ virtual void getClippingFrustum(uint cid, NL3D::UCamera *camera) const;
+
+ /// Is there a next pass
+ virtual bool nextPass();
+ /// Gets the current viewport
+ virtual const NL3D::CViewport &getCurrentViewport() const;
+ /// Gets the current camera frustum
+ virtual const NL3D::CFrustum &getCurrentFrustum(uint cid) const;
+ /// Gets the current camera frustum
+ virtual void getCurrentFrustum(uint cid, NL3D::UCamera *camera) const;
+ /// Gets the current camera matrix
+ virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const;
+
+ /// At the start of a new render target
+ virtual bool wantClear();
+ /// The 3D scene
+ virtual bool wantScene();
+ /// Interface within the 3D scene
+ virtual bool wantInterface3D();
+ /// 2D Interface
+ virtual bool wantInterface2D();
+
+ /// Returns true if a new render target was set, always fase if not using render targets
+ virtual bool beginRenderTarget();
+ /// Returns true if a render target was fully drawn, always false if not using render targets
+ virtual bool endRenderTarget();
+
+
+ /// Get the HMD orientation
+ virtual NLMISC::CQuat getOrientation() const;
+
+ /// Get GUI center (1 = width, 1 = height, 0 = center)
+ virtual void getInterface2DShift(uint cid, float &x, float &y, float distance) const;
+
+ /// Set the head model, eye position relative to orientation point
+ virtual void setEyePosition(const NLMISC::CVector &v);
+ /// Get the head model, eye position relative to orientation point
+ virtual const NLMISC::CVector &getEyePosition() const;
+
+ /// Set the scale of the game in units per meter
+ virtual void setScale(float s);
+
+
+ static void listDevices(std::vector &devicesOut);
+ static bool isLibraryInUse();
+ static void releaseLibrary();
+
+
+ /// Calculates internal camera information based on the reference camera
+ void initCamera(uint cid, const NL3D::UCamera *camera);
+ /// Checks if the device used by this class was actually created
+ bool isDeviceCreated();
+
+private:
+ CStereoLibVRDevicePtr *m_DevicePtr;
+ int m_Stage;
+ int m_SubStage;
+ CViewport m_LeftViewport;
+ CViewport m_RightViewport;
+ CFrustum m_ClippingFrustum[NL_STEREO_MAX_USER_CAMERAS];
+ CFrustum m_LeftFrustum[NL_STEREO_MAX_USER_CAMERAS];
+ CFrustum m_RightFrustum[NL_STEREO_MAX_USER_CAMERAS];
+ CMatrix m_CameraMatrix[NL_STEREO_MAX_USER_CAMERAS];
+ mutable bool m_OrientationCached;
+ mutable NLMISC::CQuat m_OrientationCache;
+ UDriver *m_Driver;
+ NLMISC::CSmartPtr m_BarrelTex;
+ NL3D::CTextureUser *m_BarrelTexU;
+ NL3D::UMaterial m_BarrelMat;
+ NLMISC::CQuadUV m_BarrelQuadLeft;
+ NLMISC::CQuadUV m_BarrelQuadRight;
+ CPixelProgram *m_PixelProgram;
+ NLMISC::CVector m_EyePosition;
+ float m_Scale;
+
+}; /* class CStereoLibVR */
+
+} /* namespace NL3D */
+
+#endif /* HAVE_LIBVR */
+
+#endif /* #ifndef NL3D_STEREO_LIBVR_H */
+
+/* end of file */
diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt
index b92f58dba..2769d8e2d 100644
--- a/code/nel/src/3d/CMakeLists.txt
+++ b/code/nel/src/3d/CMakeLists.txt
@@ -696,14 +696,16 @@ SOURCE_GROUP(Stereo FILES
stereo_ovr.cpp
stereo_ovr_fp.cpp
../../include/nel/3d/stereo_ovr.h
+ stereo_libvr.cpp
+ ../../include/nel/3d/stereo_libvr.h
stereo_debugger.cpp
../../include/nel/3d/stereo_debugger.h)
NL_TARGET_LIB(nel3d ${HEADERS} ${SRC})
-INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS} ${LIBOVR_INCLUDE_DIR})
+INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS} ${LIBOVR_INCLUDE_DIR} ${LIBVR_INCLUDE_DIR})
-TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARY} ${LIBOVR_LIBRARIES})
+TARGET_LINK_LIBRARIES(nel3d nelmisc ${FREETYPE_LIBRARY} ${LIBOVR_LIBRARIES} ${LIBVR_LIBRARY})
SET_TARGET_PROPERTIES(nel3d PROPERTIES LINK_INTERFACE_LIBRARIES "")
NL_DEFAULT_PROPS(nel3d "NeL, Library: NeL 3D")
NL_ADD_RUNTIME_FLAGS(nel3d)
@@ -714,6 +716,7 @@ NL_ADD_LIB_SUFFIX(nel3d)
ADD_DEFINITIONS(${LIBXML2_DEFINITIONS})
ADD_DEFINITIONS(${LIBOVR_DEFINITIONS})
+ADD_DEFINITIONS(${LIBVR_DEFINITIONS})
IF(WITH_PCH)
ADD_NATIVE_PRECOMPILED_HEADER(nel3d ${CMAKE_CURRENT_SOURCE_DIR}/std3d.h ${CMAKE_CURRENT_SOURCE_DIR}/std3d.cpp)
diff --git a/code/nel/src/3d/stereo_display.cpp b/code/nel/src/3d/stereo_display.cpp
index 0f6d0cbfb..eace867fc 100644
--- a/code/nel/src/3d/stereo_display.cpp
+++ b/code/nel/src/3d/stereo_display.cpp
@@ -35,6 +35,7 @@
// Project includes
#include
+#include
#include
using namespace std;
@@ -78,6 +79,9 @@ void IStereoDisplay::listDevices(std::vector &devicesOut)
#ifdef HAVE_LIBOVR
CStereoOVR::listDevices(devicesOut);
#endif
+#ifdef HAVE_LIBVR
+ CStereoLibVR::listDevices(devicesOut);
+#endif
#if !FINAL_VERSION
CStereoDebugger::listDevices(devicesOut);
#endif
diff --git a/code/nel/src/3d/stereo_libvr.cpp b/code/nel/src/3d/stereo_libvr.cpp
new file mode 100644
index 000000000..8ce64e07c
--- /dev/null
+++ b/code/nel/src/3d/stereo_libvr.cpp
@@ -0,0 +1,642 @@
+/**
+ * \file stereo_libvr.cpp
+ * \brief CStereoLibVR
+ * \date 2013-08-19 19:17MT
+ * \author Thibaut Girka (ThibG)
+ * CStereoLibVR
+ */
+
+/*
+ * Copyright (C) 2013 by authors
+ *
+ * This file is part of NL3D.
+ * NL3D is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * NL3D 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 Affero General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with NL3D. If not, see
+ * .
+ */
+
+#ifdef HAVE_LIBVR
+
+#include
+#include
+#include
+
+// STL includes
+#include
+
+// External includes
+extern "C" {
+#include
+}
+
+// NeL includes
+// #include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Project includes
+
+using namespace std;
+// using namespace NLMISC;
+
+namespace NL3D {
+
+extern const char *g_StereoOVR_fp40; //TODO: what?
+extern const char *g_StereoOVR_arbfp1; //TODO: what?
+extern const char *g_StereoOVR_ps_2_0; //TODO: what?
+
+namespace {
+sint s_DeviceCounter = 0;
+};
+
+class CStereoLibVRDeviceHandle : public IStereoDeviceFactory
+{
+public:
+ // fixme: virtual destructor???
+ IStereoDisplay *createDevice() const
+ {
+ CStereoLibVR *stereo = new CStereoLibVR(this);
+ if (stereo->isDeviceCreated())
+ return stereo;
+ delete stereo;
+ return NULL;
+ }
+};
+
+class CStereoLibVRDevicePtr
+{
+public:
+ struct hmd *HMDDevice;
+ struct display_info HMDInfo;
+ float InterpupillaryDistance;
+};
+
+CStereoLibVR::CStereoLibVR(const CStereoLibVRDeviceHandle *handle) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL), m_BarrelTexU(NULL), m_PixelProgram(NULL), m_EyePosition(0.0f, 0.09f, 0.15f), m_Scale(1.0f)
+{
+ struct stereo_config st_conf;
+
+ ++s_DeviceCounter;
+ // For now, LibVR doesn't support multiple devices...
+ m_DevicePtr = new CStereoLibVRDevicePtr();
+ m_DevicePtr->HMDDevice = hmd_open_first(0);
+ m_DevicePtr->InterpupillaryDistance = 0.0647; //TODO
+
+ if (m_DevicePtr->HMDDevice)
+ {
+ hmd_get_display_info(m_DevicePtr->HMDDevice, &m_DevicePtr->HMDInfo);
+ hmd_get_stereo_config(m_DevicePtr->HMDDevice, &st_conf);
+ nldebug("LibVR: HScreenSize: %f, VScreenSize: %f", m_DevicePtr->HMDInfo.h_screen_size, m_DevicePtr->HMDInfo.v_screen_size);
+ nldebug("LibVR: VScreenCenter: %f", m_DevicePtr->HMDInfo.v_center);
+ nldebug("LibVR: EyeToScreenDistance: %f", m_DevicePtr->HMDInfo.eye_to_screen[0]);
+ nldebug("LibVR: LensSeparationDistance: %f", m_DevicePtr->HMDInfo.lens_separation);
+ nldebug("LibVR: HResolution: %i, VResolution: %i", m_DevicePtr->HMDInfo.h_resolution, m_DevicePtr->HMDInfo.v_resolution);
+ nldebug("LibVR: DistortionK[0]: %f, DistortionK[1]: %f", m_DevicePtr->HMDInfo.distortion_k[0], m_DevicePtr->HMDInfo.distortion_k[1]);
+ nldebug("LibVR: DistortionK[2]: %f, DistortionK[3]: %f", m_DevicePtr->HMDInfo.distortion_k[2], m_DevicePtr->HMDInfo.distortion_k[3]);
+ nldebug("LibVR: Scale: %f", st_conf.distort.scale);
+ m_LeftViewport.init(0.f, 0.f, 0.5f, 1.0f);
+ m_RightViewport.init(0.5f, 0.f, 0.5f, 1.0f);
+ }
+}
+
+CStereoLibVR::~CStereoLibVR()
+{
+ if (!m_BarrelMat.empty())
+ {
+ m_BarrelMat.getObjectPtr()->setTexture(0, NULL);
+ m_Driver->deleteMaterial(m_BarrelMat);
+ }
+ delete m_BarrelTexU;
+ m_BarrelTexU = NULL;
+ m_BarrelTex = NULL; // CSmartPtr
+
+ delete m_PixelProgram;
+ m_PixelProgram = NULL;
+
+ m_Driver = NULL;
+
+ if (m_DevicePtr->HMDDevice)
+ hmd_close(m_DevicePtr->HMDDevice);
+
+ delete m_DevicePtr;
+ m_DevicePtr = NULL;
+
+ --s_DeviceCounter;
+}
+
+void CStereoLibVR::setDriver(NL3D::UDriver *driver)
+{
+ nlassert(!m_PixelProgram);
+
+ NL3D::IDriver *drvInternal = (static_cast(driver))->getDriver();
+ if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures())
+ {
+ nldebug("VR: fp40");
+ m_PixelProgram = new CPixelProgram(g_StereoOVR_fp40);
+ }
+ else if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures())
+ {
+ nldebug("VR: arbfp1");
+ m_PixelProgram = new CPixelProgram(g_StereoOVR_arbfp1);
+ }
+ else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0))
+ {
+ nldebug("VR: ps_2_0");
+ m_PixelProgram = new CPixelProgram(g_StereoOVR_ps_2_0);
+ }
+
+ if (m_PixelProgram)
+ {
+ m_Driver = driver;
+
+ m_BarrelTex = new CTextureBloom(); // lol bloom
+ m_BarrelTex->setRenderTarget(true);
+ m_BarrelTex->setReleasable(false);
+ m_BarrelTex->resize(m_DevicePtr->HMDInfo.h_resolution, m_DevicePtr->HMDInfo.v_resolution);
+ m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
+ m_BarrelTex->setWrapS(ITexture::Clamp);
+ m_BarrelTex->setWrapT(ITexture::Clamp);
+ drvInternal->setupTexture(*m_BarrelTex);
+ m_BarrelTexU = new CTextureUser(m_BarrelTex);
+
+ m_BarrelMat = m_Driver->createMaterial();
+ m_BarrelMat.initUnlit();
+ m_BarrelMat.setColor(CRGBA::White);
+ m_BarrelMat.setBlend (false);
+ m_BarrelMat.setAlphaTest (false);
+ NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr();
+ barrelMat->setShader(NL3D::CMaterial::PostProcessing);
+ barrelMat->setBlendFunc(CMaterial::one, CMaterial::zero);
+ barrelMat->setZWrite(false);
+ barrelMat->setZFunc(CMaterial::always);
+ barrelMat->setDoubleSided(true);
+ barrelMat->setTexture(0, m_BarrelTex);
+
+ m_BarrelQuadLeft.V0 = CVector(0.f, 0.f, 0.5f);
+ m_BarrelQuadLeft.V1 = CVector(0.5f, 0.f, 0.5f);
+ m_BarrelQuadLeft.V2 = CVector(0.5f, 1.f, 0.5f);
+ m_BarrelQuadLeft.V3 = CVector(0.f, 1.f, 0.5f);
+
+ m_BarrelQuadRight.V0 = CVector(0.5f, 0.f, 0.5f);
+ m_BarrelQuadRight.V1 = CVector(1.f, 0.f, 0.5f);
+ m_BarrelQuadRight.V2 = CVector(1.f, 1.f, 0.5f);
+ m_BarrelQuadRight.V3 = CVector(0.5f, 1.f, 0.5f);
+
+ nlassert(!drvInternal->isTextureRectangle(m_BarrelTex)); // not allowed
+
+ m_BarrelQuadLeft.Uv0 = CUV(0.f, 0.f);
+ m_BarrelQuadLeft.Uv1 = CUV(0.5f, 0.f);
+ m_BarrelQuadLeft.Uv2 = CUV(0.5f, 1.f);
+ m_BarrelQuadLeft.Uv3 = CUV(0.f, 1.f);
+
+ m_BarrelQuadRight.Uv0 = CUV(0.5f, 0.f);
+ m_BarrelQuadRight.Uv1 = CUV(1.f, 0.f);
+ m_BarrelQuadRight.Uv2 = CUV(1.f, 1.f);
+ m_BarrelQuadRight.Uv3 = CUV(0.5f, 1.f);
+ }
+ else
+ {
+ nlwarning("VR: No pixel program support");
+ }
+}
+
+bool CStereoLibVR::getScreenResolution(uint &width, uint &height)
+{
+ width = m_DevicePtr->HMDInfo.h_resolution;
+ height = m_DevicePtr->HMDInfo.v_resolution;
+ return true;
+}
+
+void CStereoLibVR::initCamera(uint cid, const NL3D::UCamera *camera)
+{
+ struct stereo_config st_conf;
+ hmd_get_stereo_config(m_DevicePtr->HMDDevice, &st_conf);
+
+ float ar = st_conf.proj.aspect_ratio;
+ float fov = st_conf.proj.yfov;
+ m_LeftFrustum[cid].initPerspective(fov, ar, camera->getFrustum().Near, camera->getFrustum().Far);
+ m_RightFrustum[cid] = m_LeftFrustum[cid];
+
+ float projectionCenterOffset = st_conf.proj.projection_offset * 0.5 * (m_LeftFrustum[cid].Right - m_LeftFrustum[cid].Left);
+ nldebug("LibVR: projectionCenterOffset = %f", projectionCenterOffset);
+
+ m_LeftFrustum[cid].Left -= projectionCenterOffset;
+ m_LeftFrustum[cid].Right -= projectionCenterOffset;
+ m_RightFrustum[cid].Left += projectionCenterOffset;
+ m_RightFrustum[cid].Right += projectionCenterOffset;
+
+ // TODO: Clipping frustum should also take into account the IPD
+ m_ClippingFrustum[cid] = m_LeftFrustum[cid];
+ m_ClippingFrustum[cid].Left = min(m_LeftFrustum[cid].Left, m_RightFrustum[cid].Left);
+ m_ClippingFrustum[cid].Right = max(m_LeftFrustum[cid].Right, m_RightFrustum[cid].Right);
+}
+
+/// Get the frustum to use for clipping
+void CStereoLibVR::getClippingFrustum(uint cid, NL3D::UCamera *camera) const
+{
+ camera->setFrustum(m_ClippingFrustum[cid]);
+}
+
+void CStereoLibVR::updateCamera(uint cid, const NL3D::UCamera *camera)
+{
+ if (camera->getFrustum().Near != m_LeftFrustum[cid].Near
+ || camera->getFrustum().Far != m_LeftFrustum[cid].Far)
+ CStereoLibVR::initCamera(cid, camera);
+ m_CameraMatrix[cid] = camera->getMatrix();
+}
+
+bool CStereoLibVR::nextPass()
+{
+ // Do not allow weird stuff.
+ uint32 width, height;
+ m_Driver->getWindowSize(width, height);
+ nlassert(width == m_DevicePtr->HMDInfo.h_resolution);
+ nlassert(height == m_DevicePtr->HMDInfo.v_resolution);
+
+ if (m_Driver->getPolygonMode() == UDriver::Filled)
+ {
+ switch (m_Stage)
+ {
+ case 0:
+ ++m_Stage;
+ m_SubStage = 0;
+ // stage 1:
+ // (initBloom)
+ // clear buffer
+ // draw scene left
+ return true;
+ case 1:
+ ++m_Stage;
+ m_SubStage = 0;
+ // stage 2:
+ // draw scene right
+ return true;
+ case 2:
+ ++m_Stage;
+ m_SubStage = 0;
+ // stage 3:
+ // (endBloom)
+ // draw interface 3d left
+ return true;
+ case 3:
+ ++m_Stage;
+ m_SubStage = 0;
+ // stage 4:
+ // draw interface 3d right
+ return true;
+ case 4:
+ ++m_Stage;
+ m_SubStage = 0;
+ // stage 5:
+ // (endInterfacesDisplayBloom)
+ // draw interface 2d left
+ return true;
+ case 5:
+ ++m_Stage;
+ m_SubStage = 0;
+ // stage 6:
+ // draw interface 2d right
+ return true;
+ case 6:
+ m_Stage = 0;
+ m_SubStage = 0;
+ // present
+ m_OrientationCached = false;
+ return false;
+ }
+ }
+ else
+ {
+ switch (m_Stage)
+ {
+ case 0:
+ ++m_Stage;
+ m_SubStage = 0;
+ return true;
+ case 1:
+ m_Stage = 0;
+ m_SubStage = 0;
+ return false;
+ }
+ }
+ nlerror("Invalid stage");
+ m_Stage = 0;
+ m_SubStage = 0;
+ m_OrientationCached = false;
+ return false;
+}
+
+const NL3D::CViewport &CStereoLibVR::getCurrentViewport() const
+{
+ if (m_Stage % 2) return m_LeftViewport;
+ else return m_RightViewport;
+}
+
+const NL3D::CFrustum &CStereoLibVR::getCurrentFrustum(uint cid) const
+{
+ if (m_Stage % 2) return m_LeftFrustum[cid];
+ else return m_RightFrustum[cid];
+}
+
+void CStereoLibVR::getCurrentFrustum(uint cid, NL3D::UCamera *camera) const
+{
+ if (m_Stage % 2) camera->setFrustum(m_LeftFrustum[cid]);
+ else camera->setFrustum(m_RightFrustum[cid]);
+}
+
+void CStereoLibVR::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const
+{
+ CMatrix translate;
+ if (m_Stage % 2) translate.translate(CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * -0.5f, 0.f, 0.f));
+ else translate.translate(CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * 0.5f, 0.f, 0.f));
+ CMatrix mat = m_CameraMatrix[cid] * translate;
+ if (camera->getTransformMode() == NL3D::UTransformable::RotQuat)
+ {
+ camera->setPos(mat.getPos());
+ camera->setRotQuat(mat.getRot());
+ }
+ else
+ {
+ // camera->setTransformMode(NL3D::UTransformable::DirectMatrix);
+ camera->setMatrix(mat);
+ }
+}
+
+bool CStereoLibVR::wantClear()
+{
+ switch (m_Stage)
+ {
+ case 1:
+ m_SubStage = 1;
+ return true;
+ }
+ return m_Driver->getPolygonMode() != UDriver::Filled;
+}
+
+bool CStereoLibVR::wantScene()
+{
+ switch (m_Stage)
+ {
+ case 1:
+ case 2:
+ m_SubStage = 2;
+ return true;
+ }
+ return m_Driver->getPolygonMode() != UDriver::Filled;
+}
+
+bool CStereoLibVR::wantInterface3D()
+{
+ switch (m_Stage)
+ {
+ case 3:
+ case 4:
+ m_SubStage = 3;
+ return true;
+ }
+ return m_Driver->getPolygonMode() != UDriver::Filled;
+}
+
+bool CStereoLibVR::wantInterface2D()
+{
+ switch (m_Stage)
+ {
+ case 5:
+ case 6:
+ m_SubStage = 4;
+ return true;
+ }
+ return m_Driver->getPolygonMode() != UDriver::Filled;
+}
+
+
+/// Returns non-NULL if a new render target was set
+bool CStereoLibVR::beginRenderTarget()
+{
+ // render target always set before driver clear
+ // nlassert(m_SubStage <= 1);
+ if (m_Driver && m_Stage == 1 && (m_Driver->getPolygonMode() == UDriver::Filled))
+ {
+ static_cast(m_Driver)->setRenderTarget(*m_BarrelTexU, 0, 0, 0, 0);
+ return true;
+ }
+ return false;
+}
+
+/// Returns true if a render target was fully drawn
+bool CStereoLibVR::endRenderTarget()
+{
+ // after rendering of course
+ // nlassert(m_SubStage > 1);
+ if (m_Driver && m_Stage == 6 && (m_Driver->getPolygonMode() == UDriver::Filled)) // set to 4 to turn off distortion of 2d gui
+ {
+ struct stereo_config st_conf;
+ hmd_get_stereo_config(m_DevicePtr->HMDDevice, &st_conf);
+ CTextureUser cu;
+ (static_cast(m_Driver))->setRenderTarget(cu);
+ bool fogEnabled = m_Driver->fogEnabled();
+ m_Driver->enableFog(false);
+
+ m_Driver->setMatrixMode2D11();
+ CViewport vp = CViewport();
+ m_Driver->setViewport(vp);
+ uint32 width, height;
+ m_Driver->getWindowSize(width, height);
+ NL3D::IDriver *drvInternal = (static_cast(m_Driver))->getDriver();
+ NL3D::CMaterial *barrelMat = m_BarrelMat.getObjectPtr();
+ barrelMat->setTexture(0, m_BarrelTex);
+ drvInternal->activePixelProgram(m_PixelProgram);
+
+ float w = float(m_BarrelQuadLeft.V1.x),// / float(width),
+ h = float(m_BarrelQuadLeft.V2.y),// / float(height),
+ x = float(m_BarrelQuadLeft.V0.x),/// / float(width),
+ y = float(m_BarrelQuadLeft.V0.y);// / float(height);
+
+ //TODO: stereo_config stuff
+ float lensViewportShift = st_conf.proj.projection_offset;
+
+ float lensCenterX = x + (w + lensViewportShift * 0.5f) * 0.5f;
+ float lensCenterY = y + h * 0.5f;
+ float screenCenterX = x + w * 0.5f;
+ float screenCenterY = y + h * 0.5f;
+ float scaleX = (w / 2 / st_conf.distort.scale);
+ float scaleY = (h / 2 / st_conf.distort.scale);
+ float scaleInX = (2 / w);
+ float scaleInY = (2 / h);
+ drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f);
+ drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f);
+ drvInternal->setPixelProgramConstant(2, scaleX, scaleY, 0.f, 0.f);
+ drvInternal->setPixelProgramConstant(3, scaleInX, scaleInY, 0.f, 0.f);
+ drvInternal->setPixelProgramConstant(4, 1, st_conf.distort.distortion_k);
+
+
+ m_Driver->drawQuad(m_BarrelQuadLeft, m_BarrelMat);
+
+ x = w;
+ lensCenterX = x + (w - lensViewportShift * 0.5f) * 0.5f;
+ screenCenterX = x + w * 0.5f;
+ drvInternal->setPixelProgramConstant(0, lensCenterX, lensCenterY, 0.f, 0.f);
+ drvInternal->setPixelProgramConstant(1, screenCenterX, screenCenterY, 0.f, 0.f);
+
+ m_Driver->drawQuad(m_BarrelQuadRight, m_BarrelMat);
+
+ drvInternal->activePixelProgram(NULL);
+ m_Driver->enableFog(fogEnabled);
+
+ return true;
+ }
+ return false;
+}
+
+NLMISC::CQuat CStereoLibVR::getOrientation() const
+{
+ if (m_OrientationCached)
+ return m_OrientationCache;
+
+ unsigned int t = NLMISC::CTime::getLocalTime();
+ hmd_update(m_DevicePtr->HMDDevice, &t);
+
+ float quat[4];
+ hmd_get_rotation(m_DevicePtr->HMDDevice, quat);
+ NLMISC::CMatrix coordsys;
+ float csys[] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ };
+ coordsys.set(csys);
+ NLMISC::CMatrix matovr;
+ matovr.setRot(NLMISC::CQuat(quat[1], quat[2], quat[3], quat[0]));
+ NLMISC::CMatrix matr;
+ matr.rotateX(NLMISC::Pi * 0.5f); // fix this properly... :) (note: removing this allows you to use rift while lying down)
+ NLMISC::CMatrix matnel = matr * matovr * coordsys;
+ NLMISC::CQuat finalquat = matnel.getRot();
+ m_OrientationCache = finalquat;
+ m_OrientationCached = true;
+ return finalquat;
+}
+
+/// Get GUI shift
+void CStereoLibVR::getInterface2DShift(uint cid, float &x, float &y, float distance) const
+{
+#if 0
+
+ // todo: take into account m_EyePosition
+
+ NLMISC::CVector vector = CVector(0.f, -distance, 0.f);
+ NLMISC::CQuat rot = getOrientation();
+ rot.invert();
+ NLMISC::CMatrix mat;
+ mat.rotate(rot);
+ //if (m_Stage % 2) mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f));
+ //else mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f));
+ mat.translate(vector);
+ CVector proj = CStereoOVR::getCurrentFrustum(cid).project(mat.getPos());
+
+ NLMISC::CVector ipd;
+ if (m_Stage % 2) ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f);
+ else ipd = CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f);
+ CVector projipd = CStereoOVR::getCurrentFrustum(cid).project(vector + ipd);
+ CVector projvec = CStereoOVR::getCurrentFrustum(cid).project(vector);
+
+ x = (proj.x + projipd.x - projvec.x - 0.5f);
+ y = (proj.y + projipd.y - projvec.y - 0.5f);
+
+#elif 1
+
+ // Alternative method
+ // todo: take into account m_EyePosition
+
+ NLMISC::CVector vec = CVector(0.f, -distance, 0.f);
+ NLMISC::CVector ipd;
+ if (m_Stage % 2) ipd = CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * -0.5f, 0.f, 0.f);
+ else ipd = CVector((m_DevicePtr->InterpupillaryDistance * m_Scale) * 0.5f, 0.f, 0.f);
+
+
+ NLMISC::CQuat rot = getOrientation();
+ NLMISC::CQuat modrot = NLMISC::CQuat(CVector(0.f, 1.f, 0.f), NLMISC::Pi);
+ rot = rot * modrot;
+ float p = NLMISC::Pi + atan2f(2.0f * ((rot.x * rot.y) + (rot.z * rot.w)), 1.0f - 2.0f * ((rot.y * rot.y) + (rot.w * rot.w)));
+ if (p > NLMISC::Pi) p -= NLMISC::Pi * 2.0f;
+ float t = -atan2f(2.0f * ((rot.x * rot.w) + (rot.y * rot.z)), 1.0f - 2.0f * ((rot.z * rot.z) + (rot.w * rot.w)));// // asinf(2.0f * ((rot.x * rot.z) - (rot.w * rot.y)));
+
+ CVector rotshift = CVector(p, 0.f, t) * -distance;
+
+ CVector proj = CStereoLibVR::getCurrentFrustum(cid).project(vec + ipd + rotshift);
+
+ x = (proj.x - 0.5f);
+ y = (proj.y - 0.5f);
+
+#endif
+}
+
+void CStereoLibVR::setEyePosition(const NLMISC::CVector &v)
+{
+ m_EyePosition = v;
+}
+
+const NLMISC::CVector &CStereoLibVR::getEyePosition() const
+{
+ return m_EyePosition;
+}
+
+void CStereoLibVR::setScale(float s)
+{
+ m_EyePosition = m_EyePosition * (s / m_Scale);
+ m_Scale = s;
+}
+
+void CStereoLibVR::listDevices(std::vector &devicesOut)
+{
+ // For now, LibVR doesn't support multiple devices
+ struct hmd *hmd = hmd_open_first(0);
+ if (hmd)
+ {
+ CStereoDeviceInfo deviceInfoOut;
+ CStereoLibVRDeviceHandle *handle = new CStereoLibVRDeviceHandle();
+ deviceInfoOut.Factory = static_cast(handle);
+ deviceInfoOut.Class = CStereoDeviceInfo::StereoHMD;
+ deviceInfoOut.Library = CStereoDeviceInfo::LibVR;
+ //TODO: manufacturer, produc name
+ //TODO: serial
+ devicesOut.push_back(deviceInfoOut);
+ hmd_close(hmd);
+ }
+}
+
+bool CStereoLibVR::isLibraryInUse()
+{
+ nlassert(s_DeviceCounter >= 0);
+ return s_DeviceCounter > 0;
+}
+
+void CStereoLibVR::releaseLibrary()
+{
+ nlassert(s_DeviceCounter == 0);
+}
+
+bool CStereoLibVR::isDeviceCreated()
+{
+ return m_DevicePtr->HMDDevice != NULL;
+}
+
+} /* namespace NL3D */
+
+#endif /* HAVE_LIBVR */
+
+/* end of file */