diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index b394b49d7d..edf55c90c8 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -37,7 +37,6 @@ include(../dep_deng1.pri) include(../dep_shell.pri) include(../dep_gui.pri) include(../dep_appfw.pri) -include(../dep_rift.pri) # Definitions ---------------------------------------------------------------- diff --git a/doomsday/client/include/render/vr.h b/doomsday/client/include/render/vr.h index 2eb2495723..51c9617931 100644 --- a/doomsday/client/include/render/vr.h +++ b/doomsday/client/include/render/vr.h @@ -22,7 +22,7 @@ #include "dd_types.h" -#include +#include namespace de { @@ -55,61 +55,11 @@ class VRConfig NUM_STEREO_MODES }; - /** - * State parameters for Oculus Rift. - */ - class OculusRift { - public: - OculusRift(); - - bool init(); - - void deinit(); - - void setRiftLatency(float latency); - - // True if Oculus Rift is enabled and can report head orientation. - bool isReady() const; - - void update(); - - // Called to allow head orientation to change again. - void allowUpdate(); - - // Returns current pitch, roll, yaw angles, in radians. If no head tracking is available, - // the returned values are not valid. - Vector3f headOrientation() const; - - float interpupillaryDistance() const; - - // Use screen size instead of resolution in case non-square pixels? - float aspect() const { return 0.5f * _screenSize.x / _screenSize.y; } - Vector2f const &screenSize() const { return _screenSize; } - Vector4f const &chromAbParam() const { return _chromAbParam; } - float distortionScale() const; - float fovX() const; // in degrees - float fovY() const; // in degrees - Vector4f const &hmdWarpParam() const { return _hmdWarpParam; } - float lensSeparationDistance() const { return _lensSeparationDistance; } - - private: - DENG2_PRIVATE(d) - - Vector2f _screenSize; - float _lensSeparationDistance; - Vector4f _hmdWarpParam; - Vector4f _chromAbParam; - float _eyeToScreenDistance; - - public: - float riftLatency; - }; - public: VRConfig(); - OculusRift &ovr(); - OculusRift const &ovr() const; + OculusRift &oculusRift(); + OculusRift const &oculusRift() const; /// Currently active stereo rendering mode. StereoMode mode() const; @@ -157,9 +107,6 @@ void consoleRegister(); float riftFovX(); ///< Horizontal field of view in Oculus Rift in degrees -// To release memory and resources when done, for tidiness. -//void deleteOculusTracker(); - // Load Oculus Rift parameters via Rift SDK bool loadRiftParameters(); diff --git a/doomsday/client/src/dd_main.cpp b/doomsday/client/src/dd_main.cpp index 13679bcd31..b0bf4e7f06 100644 --- a/doomsday/client/src/dd_main.cpp +++ b/doomsday/client/src/dd_main.cpp @@ -2372,7 +2372,7 @@ int DD_GetInteger(int ddvalue) return (int) GL_PrepareLSTexture(LST_DYNAMIC); case DD_USING_HEAD_TRACKING: - return vrCfg.mode() == VRConfig::ModeOculusRift && vrCfg.ovr().isReady(); + return vrCfg.mode() == VRConfig::ModeOculusRift && vrCfg.oculusRift().isReady(); #endif case DD_NUMLUMPS: diff --git a/doomsday/client/src/gl/gl_main.cpp b/doomsday/client/src/gl/gl_main.cpp index a77afa66e0..dd5500372a 100644 --- a/doomsday/client/src/gl/gl_main.cpp +++ b/doomsday/client/src/gl/gl_main.cpp @@ -564,7 +564,7 @@ Matrix4f GL_GetProjectionMatrix() if (vrCfg.mode() == VRConfig::ModeOculusRift) { - aspect = vrCfg.ovr().aspect(); + aspect = vrCfg.oculusRift().aspect(); // A little trigonometry to apply aspect ratio to angles float x = tan(0.5 * de::degreeToRadian(Rend_FieldOfView())); yfov = de::radianToDegree(2.0 * atan2(x/aspect, 1.0f)); diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index 112a0e130a..c3296333a7 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -540,9 +540,9 @@ Matrix4f Rend_GetModelViewMatrix(int consoleNum, bool useAngles) * these values and is syncing with them independently (however, game has more * latency). */ - if((vrCfg.mode() == VRConfig::ModeOculusRift) && vrCfg.ovr().isReady()) + if((vrCfg.mode() == VRConfig::ModeOculusRift) && vrCfg.oculusRift().isReady()) { - Vector3f const pry = vrCfg.ovr().headOrientation(); + Vector3f const pry = vrCfg.oculusRift().headOrientation(); // Use angles directly from the Rift for best response. roll = -radianToDegree(pry[1]); diff --git a/doomsday/client/src/render/viewports.cpp b/doomsday/client/src/render/viewports.cpp index 99e6c9f801..184f45031b 100644 --- a/doomsday/client/src/render/viewports.cpp +++ b/doomsday/client/src/render/viewports.cpp @@ -872,7 +872,7 @@ DENG_EXTERN_C void R_RenderPlayerView(int num) // Latest possible time to check the real head angles. After this we'll be // using the provided values. - vrCfg.ovr().update(); + vrCfg.oculusRift().update(); R_SetupPlayerSprites(); @@ -1324,7 +1324,7 @@ angle_t viewer_t::angle() const { // Apply the actual, current yaw offset. The game has omitted the "body yaw" // portion from the value already. - a += (fixed_t)(radianToDegree(vrCfg.ovr().headOrientation()[2]) / 180 * ANGLE_180); + a += (fixed_t)(radianToDegree(vrCfg.oculusRift().headOrientation()[2]) / 180 * ANGLE_180); } return a; } diff --git a/doomsday/client/src/render/vr.cpp b/doomsday/client/src/render/vr.cpp index a0747e6e96..f9fe4e5161 100644 --- a/doomsday/client/src/render/vr.cpp +++ b/doomsday/client/src/render/vr.cpp @@ -20,100 +20,10 @@ #include "de_console.h" #include "render/vr.h" -#ifdef DENG_HAVE_OCULUS_API -# include -#endif +#include namespace de { -#ifdef DENG_HAVE_OCULUS_API -class OculusTracker -{ -public: - OculusTracker() - : pitch(0) - , roll(0) - , yaw(0) - , _latency(0) - { - OVR::System::Init(); - _fusionResult = new OVR::SensorFusion(); - _manager = *OVR::DeviceManager::Create(); - _hmd = *_manager->EnumerateDevices().CreateDevice(); - if(_hmd) - { - _infoLoaded = _hmd->GetDeviceInfo(&_info); - _sensor = _hmd->GetSensor(); - } - else - { - _sensor = *_manager->EnumerateDevices().CreateDevice(); - } - - if (_sensor) - { - _fusionResult->AttachToSensor(_sensor); - } - } - - ~OculusTracker() - { - _sensor.Clear(); - _hmd.Clear(); - _manager.Clear(); - delete _fusionResult; - OVR::System::Destroy(); - } - - OVR::HMDInfo const &getInfo() const { - return _info; - } - - bool isGood() const { - return _sensor.GetPtr() != NULL; - } - - void update() - { - OVR::Quatf quaternion; - if (_latency == 0) - quaternion = _fusionResult->GetOrientation(); - else - quaternion = _fusionResult->GetPredictedOrientation(); - quaternion.GetEulerAngles(&yaw, &pitch, &roll); - } - - void setLatency(float lat) - { - if (_latency == lat) - return; // no change - _latency = lat; - if (_latency == 0) - { - _fusionResult->SetPredictionEnabled(false); - _fusionResult->SetPrediction(_latency); - } - else - { - _fusionResult->SetPredictionEnabled(true); - _fusionResult->SetPrediction(_latency); - } - } - - // Head orientation state, refreshed by call to update(); - float pitch, roll, yaw; - -private: - OVR::Ptr _manager; - OVR::Ptr _hmd; - OVR::Ptr _sensor; - OVR::SensorFusion* _fusionResult; - OVR::HMDInfo _info; - bool _infoLoaded; - float _latency; -}; -#endif - DENG2_PIMPL(VRConfig) { OculusRift ovr; @@ -139,185 +49,16 @@ VRConfig::VRConfig() , riftFramebufferSamples(2) {} -VRConfig::OculusRift &VRConfig::ovr() +OculusRift &VRConfig::oculusRift() { return d->ovr; } -VRConfig::OculusRift const &VRConfig::ovr() const +OculusRift const &VRConfig::oculusRift() const { return d->ovr; } -struct VRConfig::OculusRift::Instance : public Private -{ - float ipd; - bool headOrientationUpdateIsAllowed; - -#ifdef DENG_HAVE_OCULUS_API - OculusTracker *oculusTracker; -#endif - - Instance(VRConfig::OculusRift *i) - : Private(i) - , ipd(.064f) - , headOrientationUpdateIsAllowed(true) -#ifdef DENG_HAVE_OCULUS_API - , oculusTracker(0) -#endif - {} - - ~Instance() - { - self.deinit(); - } - - bool init() - { -#ifdef DENG_HAVE_OCULUS_API - if(oculusTracker) - { - return oculusTracker->isGood(); // Already inited. - } - - oculusTracker = new OculusTracker; - - if(oculusTracker->isGood() /*&& autoLoadRiftParams*/) - { - OVR::HMDInfo const &info = oculusTracker->getInfo(); - ipd = info.InterpupillaryDistance; - self._screenSize = Vector2f(info.HScreenSize, info.VScreenSize); - self._lensSeparationDistance = info.LensSeparationDistance; - self._hmdWarpParam = Vector4f( - info.DistortionK[0], - info.DistortionK[1], - info.DistortionK[2], - info.DistortionK[3]); - self._chromAbParam = Vector4f( - info.ChromaAbCorrection[0], - info.ChromaAbCorrection[1], - info.ChromaAbCorrection[2], - info.ChromaAbCorrection[3]); - self._eyeToScreenDistance = info.EyeToScreenDistance; - return true; - } - return false; -#else - return false; -#endif - } -}; - -VRConfig::OculusRift::OculusRift() - : d(new Instance(this)) - , _screenSize(0.14976f, 0.09360f) - , _lensSeparationDistance(0.0635f) - , _hmdWarpParam(1.0f, 0.220f, 0.240f, 0.000f) - , _chromAbParam(0.996f, -0.004f, 1.014f, 0.0f) - , _eyeToScreenDistance(0.041f) - , riftLatency(.030f) -{} - -bool VRConfig::OculusRift::init() -{ - return d->init(); -} - -void VRConfig::OculusRift::deinit() -{ -#ifdef DENG_HAVE_OCULUS_API - delete d->oculusTracker; - d->oculusTracker = 0; -#endif -} - -float VRConfig::OculusRift::interpupillaryDistance() const -{ - return d->ipd; -} - -void VRConfig::OculusRift::setRiftLatency(float latency) -{ -#ifdef DENG_HAVE_OCULUS_API - if(isReady()) - { - d->oculusTracker->setLatency(latency); - } -#endif -} - -// True if Oculus Rift is enabled and can report head orientation. -bool VRConfig::OculusRift::isReady() const -{ -#ifdef DENG_HAVE_OCULUS_API - if(!d->oculusTracker) - { - if(!d->init()) return false; - } - return d->oculusTracker->isGood(); -#else - // No API; No head tracking. - return false; -#endif -} - -void VRConfig::OculusRift::allowUpdate() -{ - d->headOrientationUpdateIsAllowed = true; -} - -void VRConfig::OculusRift::update() -{ -#ifdef DENG_HAVE_OCULUS_API - if(d->headOrientationUpdateIsAllowed && isReady()) - { - d->oculusTracker->update(); - d->headOrientationUpdateIsAllowed = false; - } -#endif -} - -Vector3f VRConfig::OculusRift::headOrientation() const -{ - de::Vector3f result; -#ifdef DENG_HAVE_OCULUS_API - if(isReady()) - { - result[0] = d->oculusTracker->pitch; - result[1] = d->oculusTracker->roll; - result[2] = d->oculusTracker->yaw; - } -#endif - return result; -} - -float VRConfig::OculusRift::distortionScale() const -{ - float lensShift = _screenSize.x * 0.25f - lensSeparationDistance() * 0.5f; - float lensViewportShift = 4.0f * lensShift / _screenSize.x; - float fitRadius = fabs(-1 - lensViewportShift); - float rsq = fitRadius*fitRadius; - Vector4f k = hmdWarpParam(); - float scale = (k[0] + k[1] * rsq + k[2] * rsq * rsq + k[3] * rsq * rsq * rsq); - return scale; -} - -float VRConfig::OculusRift::fovX() const -{ - float w = 0.25 * _screenSize.x * distortionScale(); - float d = _eyeToScreenDistance; - float fov = de::radianToDegree(2.0 * atan(w/d)); - return fov; -} - -float VRConfig::OculusRift::fovY() const -{ - float w = 0.5 * _screenSize.y * distortionScale(); - float d = _eyeToScreenDistance; - float fov = de::radianToDegree(2.0 * atan(w/d)); - return fov; -} - VRConfig::StereoMode VRConfig::mode() const { return (StereoMode) vrMode; @@ -359,7 +100,7 @@ float VR::riftFovX() static void vrLatencyChanged() { - vrCfg.ovr().setRiftLatency(vrCfg.ovr().riftLatency); + vrCfg.oculusRift().setRiftLatency(vrCfg.oculusRift().riftLatency); } // Interplay among vrNonRiftFovX, vrRiftFovX, and cameraFov depends on vrMode @@ -379,7 +120,7 @@ static void vrModeChanged() Con_SetFloat("rend-camera-fov", vrRiftFovX); // Update prediction latency. - vrCfg.ovr().setRiftLatency(vrCfg.ovr().riftLatency); + vrCfg.oculusRift().setRiftLatency(vrCfg.oculusRift().riftLatency); } else { @@ -418,7 +159,7 @@ void VR::consoleRegister() C_VAR_BYTE ("rend-vr-autoload-rift-params", & autoLoadRiftParams, 0, 0, 1); C_VAR_FLOAT2("rend-vr-nonrift-fovx", & vrNonRiftFovX, 0, 5.0f, 270.0f, vrNonRiftFovXChanged); C_VAR_FLOAT2("rend-vr-rift-fovx", & vrRiftFovX, 0, 5.0f, 270.0f, vrRiftFovXChanged); - C_VAR_FLOAT2("rend-vr-rift-latency", & vrCfg.ovr().riftLatency, 0, 0.0f, 0.100f, vrLatencyChanged); + C_VAR_FLOAT2("rend-vr-rift-latency", & vrCfg.oculusRift().riftLatency, 0, 0.0f, 0.100f, vrLatencyChanged); C_VAR_FLOAT ("rend-vr-dominant-eye", & vrCfg.dominantEye, 0, -1.0f, 1.0f); C_VAR_FLOAT ("rend-vr-hud-distance", & vrCfg.hudDistance, 0, 0.01f, 40.0f); @@ -437,7 +178,7 @@ void VR::consoleRegister() bool VR::loadRiftParameters() { - de::VRConfig::OculusRift &ovr = vrCfg.ovr(); + de::OculusRift &ovr = vrCfg.oculusRift(); if(ovr.isReady()) { diff --git a/doomsday/client/src/ui/dd_input.cpp b/doomsday/client/src/ui/dd_input.cpp index 4db4e82190..1e72302ab6 100644 --- a/doomsday/client/src/ui/dd_input.cpp +++ b/doomsday/client/src/ui/dd_input.cpp @@ -1556,8 +1556,8 @@ void DD_ReadHeadTracker(void) I_GetDevice(IDEV_HEAD_TRACKER)->flags |= ID_ACTIVE; // Get the latest values. - vrCfg.ovr().allowUpdate(); - vrCfg.ovr().update(); + vrCfg.oculusRift().allowUpdate(); + vrCfg.oculusRift().update(); ddevent_t ev; @@ -1565,7 +1565,7 @@ void DD_ReadHeadTracker(void) ev.type = E_AXIS; ev.axis.type = EAXIS_ABSOLUTE; - Vector3f const pry = vrCfg.ovr().headOrientation(); + Vector3f const pry = vrCfg.oculusRift().headOrientation(); // Yaw (1.0 means 180 degrees). ev.axis.id = 0; // Yaw. diff --git a/doomsday/client/src/ui/dialogs/vrsettingsdialog.cpp b/doomsday/client/src/ui/dialogs/vrsettingsdialog.cpp index 8b2dbac85b..edd094e9e1 100644 --- a/doomsday/client/src/ui/dialogs/vrsettingsdialog.cpp +++ b/doomsday/client/src/ui/dialogs/vrsettingsdialog.cpp @@ -70,7 +70,7 @@ DENG_GUI_PIMPL(VRSettingsDialog) area.add(ipd = new CVarSliderWidget("rend-vr-ipd")); ipd->setDisplayFactor(1000); - if(vrCfg.ovr().isReady()) + if(vrCfg.oculusRift().isReady()) { area.add(riftPredictionLatency = new CVarSliderWidget("rend-vr-rift-latency")); riftPredictionLatency->setDisplayFactor(1000); @@ -121,7 +121,7 @@ VRSettingsDialog::VRSettingsDialog(String const &name) << Const(0) << *d->swapEyes << *sampleLabel << *d->riftSamples; - if(vrCfg.ovr().isReady()) + if(vrCfg.oculusRift().isReady()) { LabelWidget *ovrLabel = LabelWidget::newWithText(_E(1)_E(D) + tr("Oculus Rift"), &area()); LabelWidget *latencyLabel = LabelWidget::newWithText(tr("Prediction Latency:"), &area()); diff --git a/doomsday/client/src/ui/vrwindowtransform.cpp b/doomsday/client/src/ui/vrwindowtransform.cpp index b96847d377..718e7352b3 100644 --- a/doomsday/client/src/ui/vrwindowtransform.cpp +++ b/doomsday/client/src/ui/vrwindowtransform.cpp @@ -168,11 +168,11 @@ DENG2_PIMPL(VRWindowTransform) .setDepthTest(false); // Copy contents of offscreen buffer to normal screen. - uOculusDistortionScale = vrCfg.ovr().distortionScale(); - uOculusScreenSize = vrCfg.ovr().screenSize(); - uOculusLensSeparation = vrCfg.ovr().lensSeparationDistance(); - uOculusHmdWarpParam = vrCfg.ovr().hmdWarpParam(); - uOculusChromAbParam = vrCfg.ovr().chromAbParam(); + uOculusDistortionScale = vrCfg.oculusRift().distortionScale(); + uOculusScreenSize = vrCfg.oculusRift().screenSize(); + uOculusLensSeparation = vrCfg.oculusRift().lensSeparationDistance(); + uOculusHmdWarpParam = vrCfg.oculusRift().hmdWarpParam(); + uOculusChromAbParam = vrCfg.oculusRift().chromAbParam(); // oculusRift.draw(); @@ -216,7 +216,7 @@ Vector2ui VRWindowTransform::logicalRootSize(Vector2ui const &physicalCanvasSize case VRConfig::ModeOculusRift: /// @todo - taskbar needs to elevate above bottom of screen in Rift mode // Adjust effective UI size for stereoscopic rendering. - size.x = size.y * vrCfg.ovr().aspect(); + size.x = size.y * vrCfg.oculusRift().aspect(); size *= 1.0f; // Use a large font in taskbar break; @@ -282,7 +282,7 @@ Vector2f VRWindowTransform::windowToLogicalCoords(Vector2i const &winPos) const void VRWindowTransform::drawTransformed() { - vrCfg.ovr().allowUpdate(); + vrCfg.oculusRift().allowUpdate(); switch(vrCfg.mode()) { diff --git a/doomsday/dep_rift.pri b/doomsday/dep_rift.pri index 71f1d95e85..d511daa6e1 100644 --- a/doomsday/dep_rift.pri +++ b/doomsday/dep_rift.pri @@ -9,10 +9,10 @@ exists($${LIBOVR_DIR}/Include/OVR.h) { LIBS += shell32.lib winmm.lib } macx { - # ACK! Must rebuild libovr with RTTI (TODO) deng_debug: LIBS += $${LIBOVR_DIR}/Lib/MacOS/Debug/libovr.a else: LIBS += $${LIBOVR_DIR}/Lib/MacOS/Release/libovr.a - LIBS += -framework IOKit + useFramework(Cocoa) + useFramework(IOKit) } # For linux, you need to install libxinerama-dev and libudev-dev linux-g++|linux-g++-32 { diff --git a/doomsday/libappfw/include/de/OculusRift b/doomsday/libappfw/include/de/OculusRift new file mode 100644 index 0000000000..10291c7525 --- /dev/null +++ b/doomsday/libappfw/include/de/OculusRift @@ -0,0 +1,2 @@ +#include "vr/oculusrift.h" + diff --git a/doomsday/libappfw/include/de/framework/atlasproceduralimage.h b/doomsday/libappfw/include/de/framework/atlasproceduralimage.h index 2045937e49..f0be4dfcd4 100644 --- a/doomsday/libappfw/include/de/framework/atlasproceduralimage.h +++ b/doomsday/libappfw/include/de/framework/atlasproceduralimage.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_ATLASPROCEDURALIMAGE_H #define LIBAPPFW_ATLASPROCEDURALIMAGE_H -#include "libappfw.h" +#include "../libappfw.h" #include "../ProceduralImage" #include "../GuiWidget" #include "../GuiRootWidget" diff --git a/doomsday/libappfw/include/de/framework/baseguiapp.h b/doomsday/libappfw/include/de/framework/baseguiapp.h index 489488747e..b347e5a0ae 100644 --- a/doomsday/libappfw/include/de/framework/baseguiapp.h +++ b/doomsday/libappfw/include/de/framework/baseguiapp.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_BASEGUIAPP_H #define LIBAPPFW_BASEGUIAPP_H -#include "libappfw.h" +#include "../libappfw.h" #include #include diff --git a/doomsday/libappfw/include/de/framework/childwidgetorganizer.h b/doomsday/libappfw/include/de/framework/childwidgetorganizer.h index 344e1dcff0..e08335626f 100644 --- a/doomsday/libappfw/include/de/framework/childwidgetorganizer.h +++ b/doomsday/libappfw/include/de/framework/childwidgetorganizer.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_CHILDWIDGETORGANIZER_H #define LIBAPPFW_CHILDWIDGETORGANIZER_H -#include "libappfw.h" +#include "../libappfw.h" #include "../ui/Data" #include "../GuiWidget" diff --git a/doomsday/libappfw/include/de/framework/dialogcontentstylist.h b/doomsday/libappfw/include/de/framework/dialogcontentstylist.h index 3ce5ba954c..066e9270f1 100644 --- a/doomsday/libappfw/include/de/framework/dialogcontentstylist.h +++ b/doomsday/libappfw/include/de/framework/dialogcontentstylist.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_DIALOGCONTENTSTYLIST_H #define LIBAPPFW_DIALOGCONTENTSTYLIST_H -#include "libappfw.h" +#include "../libappfw.h" #include "../ui/Stylist" #include diff --git a/doomsday/libappfw/include/de/framework/fontlinewrapping.h b/doomsday/libappfw/include/de/framework/fontlinewrapping.h index baa04c1bd8..b0271f3114 100644 --- a/doomsday/libappfw/include/de/framework/fontlinewrapping.h +++ b/doomsday/libappfw/include/de/framework/fontlinewrapping.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_FONTLINEWRAPPING_H #define LIBAPPFW_FONTLINEWRAPPING_H -#include "libappfw.h" +#include "../libappfw.h" #include #include diff --git a/doomsday/libappfw/include/de/framework/guirootwidget.h b/doomsday/libappfw/include/de/framework/guirootwidget.h index c841cef355..de4b21c794 100644 --- a/doomsday/libappfw/include/de/framework/guirootwidget.h +++ b/doomsday/libappfw/include/de/framework/guirootwidget.h @@ -26,7 +26,7 @@ #include #include -#include "libappfw.h" +#include "../libappfw.h" namespace de { diff --git a/doomsday/libappfw/include/de/framework/item.h b/doomsday/libappfw/include/de/framework/item.h index 85b59be417..6994c679f2 100644 --- a/doomsday/libappfw/include/de/framework/item.h +++ b/doomsday/libappfw/include/de/framework/item.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_UI_DATAITEM_H #define LIBAPPFW_UI_DATAITEM_H -#include "libappfw.h" +#include "../libappfw.h" #include #include diff --git a/doomsday/libappfw/include/de/framework/proceduralimage.h b/doomsday/libappfw/include/de/framework/proceduralimage.h index 15a60fb9cd..ef3ee7c759 100644 --- a/doomsday/libappfw/include/de/framework/proceduralimage.h +++ b/doomsday/libappfw/include/de/framework/proceduralimage.h @@ -22,7 +22,7 @@ #include #include -#include "libappfw.h" +#include "../libappfw.h" namespace de { diff --git a/doomsday/libappfw/include/de/framework/signalaction.h b/doomsday/libappfw/include/de/framework/signalaction.h index 591c0ad155..046bdec936 100644 --- a/doomsday/libappfw/include/de/framework/signalaction.h +++ b/doomsday/libappfw/include/de/framework/signalaction.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_SIGNALACTION_H #define LIBAPPFW_SIGNALACTION_H -#include "libappfw.h" +#include "../libappfw.h" #include #include diff --git a/doomsday/libappfw/include/de/framework/style.h b/doomsday/libappfw/include/de/framework/style.h index 13ebc70555..3e4f2ad4b6 100644 --- a/doomsday/libappfw/include/de/framework/style.h +++ b/doomsday/libappfw/include/de/framework/style.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_STYLE_H #define LIBAPPFW_STYLE_H -#include "libappfw.h" +#include "../libappfw.h" #include #include #include diff --git a/doomsday/libappfw/include/de/framework/stylist.h b/doomsday/libappfw/include/de/framework/stylist.h index c464cbc7db..a9c43714b2 100644 --- a/doomsday/libappfw/include/de/framework/stylist.h +++ b/doomsday/libappfw/include/de/framework/stylist.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_UI_STYLIST_H #define LIBAPPFW_UI_STYLIST_H -#include "libappfw.h" +#include "../libappfw.h" namespace de { diff --git a/doomsday/libappfw/include/de/framework/libappfw.h b/doomsday/libappfw/include/de/libappfw.h similarity index 100% rename from doomsday/libappfw/include/de/framework/libappfw.h rename to doomsday/libappfw/include/de/libappfw.h diff --git a/doomsday/libappfw/include/de/ui/defs.h b/doomsday/libappfw/include/de/ui/defs.h index 875125ae9b..3bf3587018 100644 --- a/doomsday/libappfw/include/de/ui/defs.h +++ b/doomsday/libappfw/include/de/ui/defs.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_UI_DEFS_H #define LIBAPPFW_UI_DEFS_H -#include "../framework/libappfw.h" +#include "../libappfw.h" #include #include diff --git a/doomsday/libappfw/include/de/vr/oculusrift.h b/doomsday/libappfw/include/de/vr/oculusrift.h new file mode 100644 index 0000000000..0629be0617 --- /dev/null +++ b/doomsday/libappfw/include/de/vr/oculusrift.h @@ -0,0 +1,75 @@ +/** @file oculusrift.h + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 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 General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBAPPFW_OCULUSRIFT_H +#define LIBAPPFW_OCULUSRIFT_H + +#include "../libappfw.h" +#include + +namespace de { + +/** + * Oculus Rift configuration and head tracking. + */ +class LIBAPPFW_PUBLIC OculusRift +{ +public: + OculusRift(); + + bool init(); + + void deinit(); + + // True if Oculus Rift is enabled and can report head orientation. + bool isReady() const; + + void setRiftLatency(float latency); + + // Called to allow head orientation to change again. + void allowUpdate(); + + void update(); + + // Returns current pitch, roll, yaw angles, in radians. If no head tracking is available, + // the returned values are not valid. + Vector3f headOrientation() const; + + float interpupillaryDistance() const; + + // Use screen size instead of resolution in case non-square pixels? + float aspect() const; + + Vector2f screenSize() const; + Vector4f chromAbParam() const; + float distortionScale() const; + float fovX() const; // in degrees + float fovY() const; // in degrees + Vector4f hmdWarpParam() const; + float lensSeparationDistance() const; + +private: + DENG2_PRIVATE(d) + +public: + float riftLatency; /// @todo refactor away +}; + +} // namespace de + +#endif // LIBAPPFW_OCULUSRIFT_H diff --git a/doomsday/libappfw/libappfw.pro b/doomsday/libappfw/libappfw.pro index 3c617b83f5..0bceccf8cf 100644 --- a/doomsday/libappfw/libappfw.pro +++ b/doomsday/libappfw/libappfw.pro @@ -18,6 +18,7 @@ include(../dep_deng1.pri) # Garbage include(../dep_shell.pri) include(../dep_gui.pri) include(../dep_opengl.pri) +include(../dep_rift.pri) DEFINES += __LIBAPPFW__ INCLUDEPATH += include @@ -26,12 +27,6 @@ win32 { # Keep the version number out of the file name. TARGET_EXT = .dll } -else:macx { - #useFramework(Cocoa) -} -else:unix { - #LIBS += -lX11 -} # Public headers. HEADERS += \ @@ -101,7 +96,6 @@ HEADERS += \ include/de/framework/guiwidget.h \ include/de/framework/guiwidgetprivate.h \ include/de/framework/item.h \ - include/de/framework/libappfw.h \ include/de/framework/listdata.h \ include/de/framework/margins.h \ include/de/framework/proceduralimage.h \ @@ -113,7 +107,9 @@ HEADERS += \ include/de/framework/subwidgetitem.h \ include/de/framework/textdrawable.h \ include/de/framework/variabletoggleitem.h \ + include/de/libappfw.h \ include/de/ui/defs.h \ + include/de/vr/oculusrift.h \ include/de/widgets/blurwidget.h \ include/de/widgets/buttonwidget.h \ include/de/widgets/choicewidget.h \ @@ -161,6 +157,7 @@ SOURCES += \ src/signalaction.cpp \ src/style.cpp \ src/textdrawable.cpp \ + src/vr/oculusrift.cpp \ src/widgets/blurwidget.cpp \ src/widgets/buttonwidget.cpp \ src/widgets/choicewidget.cpp \ diff --git a/doomsday/libappfw/src/vr/oculusrift.cpp b/doomsday/libappfw/src/vr/oculusrift.cpp new file mode 100644 index 0000000000..d37abc0c1d --- /dev/null +++ b/doomsday/libappfw/src/vr/oculusrift.cpp @@ -0,0 +1,316 @@ +/** @file oculusrift.cpp + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 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 General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "de/OculusRift" + +#ifdef DENG_HAVE_OCULUS_API +# include +#endif + +namespace de { + +#ifdef DENG_HAVE_OCULUS_API +class OculusTracker +{ +public: + OculusTracker() + : pitch(0) + , roll(0) + , yaw(0) + , _latency(0) + { + OVR::System::Init(); + _fusionResult = new OVR::SensorFusion(); + _manager = *OVR::DeviceManager::Create(); + _hmd = *_manager->EnumerateDevices().CreateDevice(); + if(_hmd) + { + _infoLoaded = _hmd->GetDeviceInfo(&_info); + _sensor = _hmd->GetSensor(); + } + else + { + _sensor = *_manager->EnumerateDevices().CreateDevice(); + } + + if (_sensor) + { + _fusionResult->AttachToSensor(_sensor); + } + } + + ~OculusTracker() + { + _sensor.Clear(); + _hmd.Clear(); + _manager.Clear(); + delete _fusionResult; + OVR::System::Destroy(); + } + + OVR::HMDInfo const &getInfo() const { + return _info; + } + + bool isGood() const { + return _sensor.GetPtr() != NULL; + } + + void update() + { + OVR::Quatf quaternion; + if (_latency == 0) + quaternion = _fusionResult->GetOrientation(); + else + quaternion = _fusionResult->GetPredictedOrientation(); + quaternion.GetEulerAngles(&yaw, &pitch, &roll); + } + + void setLatency(float lat) + { + if (_latency == lat) + return; // no change + _latency = lat; + if (_latency == 0) + { + _fusionResult->SetPredictionEnabled(false); + _fusionResult->SetPrediction(_latency); + } + else + { + _fusionResult->SetPredictionEnabled(true); + _fusionResult->SetPrediction(_latency); + } + } + + // Head orientation state, refreshed by call to update(); + float pitch, roll, yaw; + +private: + OVR::Ptr _manager; + OVR::Ptr _hmd; + OVR::Ptr _sensor; + OVR::SensorFusion* _fusionResult; + OVR::HMDInfo _info; + bool _infoLoaded; + float _latency; +}; +#endif + +DENG2_PIMPL(OculusRift) +{ + Vector2f screenSize; + float lensSeparationDistance; + Vector4f hmdWarpParam; + Vector4f chromAbParam; + float eyeToScreenDistance; + + float ipd; + bool headOrientationUpdateIsAllowed; + +#ifdef DENG_HAVE_OCULUS_API + OculusTracker *oculusTracker; +#endif + + Instance(Public *i) + : Base(i) + , screenSize(0.14976f, 0.09360f) + , lensSeparationDistance(0.0635f) + , hmdWarpParam(1.0f, 0.220f, 0.240f, 0.000f) + , chromAbParam(0.996f, -0.004f, 1.014f, 0.0f) + , eyeToScreenDistance(0.041f) + , ipd(.064f) + , headOrientationUpdateIsAllowed(true) +#ifdef DENG_HAVE_OCULUS_API + , oculusTracker(0) +#endif + {} + + ~Instance() + { + deinit(); + } + + bool init() + { +#ifdef DENG_HAVE_OCULUS_API + if(oculusTracker) + { + return oculusTracker->isGood(); // Already inited. + } + + oculusTracker = new OculusTracker; + + if(oculusTracker->isGood() /*&& autoLoadRiftParams*/) + { + OVR::HMDInfo const &info = oculusTracker->getInfo(); + ipd = info.InterpupillaryDistance; + screenSize = Vector2f(info.HScreenSize, info.VScreenSize); + lensSeparationDistance = info.LensSeparationDistance; + hmdWarpParam = Vector4f( + info.DistortionK[0], + info.DistortionK[1], + info.DistortionK[2], + info.DistortionK[3]); + chromAbParam = Vector4f( + info.ChromaAbCorrection[0], + info.ChromaAbCorrection[1], + info.ChromaAbCorrection[2], + info.ChromaAbCorrection[3]); + eyeToScreenDistance = info.EyeToScreenDistance; + return true; + } + return false; +#else + return false; +#endif + } + + void deinit() + { +#ifdef DENG_HAVE_OCULUS_API + delete oculusTracker; + oculusTracker = 0; +#endif + } +}; + +OculusRift::OculusRift() + : d(new Instance(this)) + , riftLatency(.030f) +{} + +bool OculusRift::init() +{ + return d->init(); +} + +void OculusRift::deinit() +{ + d->deinit(); +} + +float OculusRift::interpupillaryDistance() const +{ + return d->ipd; +} + +float OculusRift::aspect() const +{ + return 0.5f * d->screenSize.x / d->screenSize.y; +} + +Vector2f OculusRift::screenSize() const +{ + return d->screenSize; +} + +Vector4f OculusRift::chromAbParam() const +{ + return d->chromAbParam; +} + +Vector4f OculusRift::hmdWarpParam() const +{ + return d->hmdWarpParam; +} + +float OculusRift::lensSeparationDistance() const +{ + return d->lensSeparationDistance; +} + +void OculusRift::setRiftLatency(float latency) +{ +#ifdef DENG_HAVE_OCULUS_API + if(isReady()) + { + d->oculusTracker->setLatency(latency); + } +#endif +} + +// True if Oculus Rift is enabled and can report head orientation. +bool OculusRift::isReady() const +{ +#ifdef DENG_HAVE_OCULUS_API + if(!d->oculusTracker) + { + if(!d->init()) return false; + } + return d->oculusTracker->isGood(); +#else + // No API; No head tracking. + return false; +#endif +} + +void OculusRift::allowUpdate() +{ + d->headOrientationUpdateIsAllowed = true; +} + +void OculusRift::update() +{ +#ifdef DENG_HAVE_OCULUS_API + if(d->headOrientationUpdateIsAllowed && isReady()) + { + d->oculusTracker->update(); + d->headOrientationUpdateIsAllowed = false; + } +#endif +} + +Vector3f OculusRift::headOrientation() const +{ + de::Vector3f result; +#ifdef DENG_HAVE_OCULUS_API + if(isReady()) + { + result[0] = d->oculusTracker->pitch; + result[1] = d->oculusTracker->roll; + result[2] = d->oculusTracker->yaw; + } +#endif + return result; +} + +float OculusRift::distortionScale() const +{ + float lensShift = d->screenSize.x * 0.25f - lensSeparationDistance() * 0.5f; + float lensViewportShift = 4.0f * lensShift / d->screenSize.x; + float fitRadius = fabs(-1 - lensViewportShift); + float rsq = fitRadius*fitRadius; + Vector4f k = hmdWarpParam(); + float scale = (k[0] + k[1] * rsq + k[2] * rsq * rsq + k[3] * rsq * rsq * rsq); + return scale; +} + +float OculusRift::fovX() const +{ + float const w = 0.25 * d->screenSize.x * distortionScale(); + return de::radianToDegree(2.0 * atan(w / d->eyeToScreenDistance)); +} + +float OculusRift::fovY() const +{ + float const w = 0.5 * d->screenSize.y * distortionScale(); + return de::radianToDegree(2.0 * atan(w / d->eyeToScreenDistance)); +} + +} // namespace de