From de7a72d82ab80af1e17edebe7c0b9d25a728cace Mon Sep 17 00:00:00 2001 From: DavidWyand-GG Date: Wed, 10 Apr 2013 01:05:26 -0400 Subject: [PATCH] Oculus VR (Rift) support Input device and shaders for supporting the Oculus Rift. --- .../oculusVR/barrelDistortionPostEffect.cpp | 188 ++++ .../oculusVR/barrelDistortionPostEffect.h | 68 ++ .../input/oculusVR/oculusVRConstants.h | 34 + .../input/oculusVR/oculusVRDevice.cpp | 844 ++++++++++++++++++ .../platform/input/oculusVR/oculusVRDevice.h | 152 ++++ .../input/oculusVR/oculusVRHMDDevice.cpp | 208 +++++ .../input/oculusVR/oculusVRHMDDevice.h | 187 ++++ .../input/oculusVR/oculusVRSensorData.cpp | 96 ++ .../input/oculusVR/oculusVRSensorData.h | 68 ++ .../input/oculusVR/oculusVRSensorDevice.cpp | 265 ++++++ .../input/oculusVR/oculusVRSensorDevice.h | 122 +++ .../platform/input/oculusVR/oculusVRUtil.cpp | 76 ++ .../platform/input/oculusVR/oculusVRUtil.h | 42 + .../Empty/game/core/scripts/client/core.cs | 3 + .../game/core/scripts/client/oculusVR.cs | 125 +++ .../client/postFx/ovrBarrelDistortion.cs | 123 +++ .../postFx/oculusvr/barrelDistortionP.hlsl | 81 ++ .../common/postFx/oculusvr/monoToStereoP.hlsl | 60 ++ .../Full/game/core/scripts/client/core.cs | 3 + .../Full/game/core/scripts/client/oculusVR.cs | 125 +++ .../client/postFx/ovrBarrelDistortion.cs | 123 +++ .../postFx/oculusvr/barrelDistortionP.hlsl | 81 ++ .../common/postFx/oculusvr/monoToStereoP.hlsl | 60 ++ Tools/projectGenerator/modules/oculusVR.inc | 80 ++ 24 files changed, 3214 insertions(+) create mode 100644 Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.cpp create mode 100644 Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.h create mode 100644 Engine/source/platform/input/oculusVR/oculusVRConstants.h create mode 100644 Engine/source/platform/input/oculusVR/oculusVRDevice.cpp create mode 100644 Engine/source/platform/input/oculusVR/oculusVRDevice.h create mode 100644 Engine/source/platform/input/oculusVR/oculusVRHMDDevice.cpp create mode 100644 Engine/source/platform/input/oculusVR/oculusVRHMDDevice.h create mode 100644 Engine/source/platform/input/oculusVR/oculusVRSensorData.cpp create mode 100644 Engine/source/platform/input/oculusVR/oculusVRSensorData.h create mode 100644 Engine/source/platform/input/oculusVR/oculusVRSensorDevice.cpp create mode 100644 Engine/source/platform/input/oculusVR/oculusVRSensorDevice.h create mode 100644 Engine/source/platform/input/oculusVR/oculusVRUtil.cpp create mode 100644 Engine/source/platform/input/oculusVR/oculusVRUtil.h create mode 100644 Templates/Empty/game/core/scripts/client/oculusVR.cs create mode 100644 Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs create mode 100644 Templates/Empty/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl create mode 100644 Templates/Empty/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl create mode 100644 Templates/Full/game/core/scripts/client/oculusVR.cs create mode 100644 Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs create mode 100644 Templates/Full/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl create mode 100644 Templates/Full/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl create mode 100644 Tools/projectGenerator/modules/oculusVR.inc diff --git a/Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.cpp b/Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.cpp new file mode 100644 index 0000000000..2ef74ac1ee --- /dev/null +++ b/Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.cpp @@ -0,0 +1,188 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/input/oculusVR/barrelDistortionPostEffect.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "platform/input/oculusVR/oculusVRDevice.h" + +extern bool gEditingMission; + +ConsoleDocClass( BarrelDistortionPostEffect, + "@brief A fullscreen shader effect used with the Oculus Rift.\n\n" + + "@section PFXTextureIdentifiers\n\n" + + "@ingroup Rendering\n" +); + +IMPLEMENT_CONOBJECT(BarrelDistortionPostEffect); + +BarrelDistortionPostEffect::BarrelDistortionPostEffect() + : PostEffect(), + mHmdWarpParamSC(NULL), + mScaleSC(NULL), + mScaleInSC(NULL), + mLensCenterSC(NULL), + mScreenCenterSC(NULL) +{ + mHMDIndex = 0; + mSensorIndex = 0; + mScaleOutput = 1.0f; +} + +BarrelDistortionPostEffect::~BarrelDistortionPostEffect() +{ +} + +void BarrelDistortionPostEffect::initPersistFields() +{ + addField( "hmdIndex", TypeS32, Offset( mHMDIndex, BarrelDistortionPostEffect ), + "Oculus VR HMD index to reference." ); + + addField( "sensorIndex", TypeS32, Offset( mSensorIndex, BarrelDistortionPostEffect ), + "Oculus VR sensor index to reference." ); + + addField( "scaleOutput", TypeF32, Offset( mScaleOutput, BarrelDistortionPostEffect ), + "Used to increase the size of the window into the world at the expense of apparent resolution." ); + + Parent::initPersistFields(); +} + +bool BarrelDistortionPostEffect::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + return true; +} + +void BarrelDistortionPostEffect::onRemove() +{ + Parent::onRemove(); +} + +void BarrelDistortionPostEffect::_setupConstants( const SceneRenderState *state ) +{ + Parent::_setupConstants(state); + + // Define the shader constants + if(!mHmdWarpParamSC) + mHmdWarpParamSC = mShader->getShaderConstHandle( "$HmdWarpParam" ); + + if(!mScaleSC) + mScaleSC = mShader->getShaderConstHandle( "$Scale" ); + + if(!mScaleInSC) + mScaleInSC = mShader->getShaderConstHandle( "$ScaleIn" ); + + if(!mLensCenterSC) + mLensCenterSC = mShader->getShaderConstHandle( "$LensCenter" ); + + if(!mScreenCenterSC) + mScreenCenterSC = mShader->getShaderConstHandle( "$ScreenCenter" ); + + const Point2I &resolution = GFX->getActiveRenderTarget()->getSize(); + F32 widthScale = 0.5f; + F32 heightScale = 1.0f; + F32 aspectRatio = (resolution.x * 0.5f) / resolution.y; + + // Set up the HMD dependant shader constants + if(ManagedSingleton::instanceOrNull() && OCULUSVRDEV->getHMDDevice(mHMDIndex)) + { + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(mHMDIndex); + + if(mHmdWarpParamSC->isValid()) + { + const Point4F& distortion = hmd->getKDistortion(); + mShaderConsts->set( mHmdWarpParamSC, distortion ); + } + + if(mScaleSC->isValid()) + { + F32 scaleFactor = hmd->getDistortionScale(); + if(!mIsZero(mScaleOutput)) + { + scaleFactor /= mScaleOutput; + } + Point2F scale; + scale.x = widthScale * 0.5f * scaleFactor; + scale.y = heightScale * 0.5f * scaleFactor * aspectRatio; + mShaderConsts->set( mScaleSC, scale ); + } + + if(mLensCenterSC->isValid()) + { + F32 xCenterOffset = hmd->getCenterOffset(); + Point3F lensCenter; + lensCenter.x = (widthScale + xCenterOffset * 0.5f) * 0.5f; + lensCenter.y = (widthScale - xCenterOffset * 0.5f) * 0.5f; + lensCenter.z = heightScale * 0.5f; + mShaderConsts->set( mLensCenterSC, lensCenter ); + } + } + else + { + if(mHmdWarpParamSC->isValid()) + { + mShaderConsts->set( mHmdWarpParamSC, Point4F(0.0f, 0.0f, 0.0f, 0.0f) ); + } + + if(mScaleSC->isValid()) + { + mShaderConsts->set( mScaleSC, Point2F(1.0f, 1.0f) ); + } + + if(mLensCenterSC->isValid()) + { + Point3F lensCenter; + lensCenter.x = widthScale * 0.5f; + lensCenter.y = widthScale * 0.5f; + lensCenter.z = heightScale * 0.5f; + mShaderConsts->set( mLensCenterSC, lensCenter ); + } + } + + if(mScaleInSC->isValid()) + { + Point2F scaleIn; + scaleIn.x = 2.0f / widthScale; + scaleIn.y = 2.0f / heightScale / aspectRatio; + mShaderConsts->set( mScaleInSC, scaleIn ); + } + + if(mScreenCenterSC->isValid()) + { + mShaderConsts->set( mScreenCenterSC, Point2F(widthScale * 0.5f, heightScale * 0.5f) ); + } +} + +void BarrelDistortionPostEffect::process(const SceneRenderState *state, GFXTexHandle &inOutTex, const RectI *inTexViewport) +{ + // Don't draw the post effect if the editor is active + if(gEditingMission) + return; + + Parent::process(state, inOutTex, inTexViewport); +} diff --git a/Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.h b/Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.h new file mode 100644 index 0000000000..389bd96ffd --- /dev/null +++ b/Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BARRELDISTORTIONPOSTEFFECT_H_ +#define _BARRELDISTORTIONPOSTEFFECT_H_ + +#include "postFx/postEffect.h" + +class BarrelDistortionPostEffect : public PostEffect +{ + typedef PostEffect Parent; + +protected: + GFXShaderConstHandle *mHmdWarpParamSC; + GFXShaderConstHandle *mScaleSC; + GFXShaderConstHandle *mScaleInSC; + GFXShaderConstHandle *mLensCenterSC; + GFXShaderConstHandle *mScreenCenterSC; + + // Oculus VR HMD index to reference + S32 mHMDIndex; + + // Oculus VR sensor index to reference + S32 mSensorIndex; + + // Used to increase the size of the window into the world at the + // expense of apparent resolution. + F32 mScaleOutput; + +protected: + virtual void _setupConstants( const SceneRenderState *state ); + +public: + BarrelDistortionPostEffect(); + virtual ~BarrelDistortionPostEffect(); + + DECLARE_CONOBJECT(BarrelDistortionPostEffect); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + + virtual void process( const SceneRenderState *state, + GFXTexHandle &inOutTex, + const RectI *inTexViewport = NULL ); +}; + +#endif // _BARRELDISTORTIONPOSTEFFECT_H_ diff --git a/Engine/source/platform/input/oculusVR/oculusVRConstants.h b/Engine/source/platform/input/oculusVR/oculusVRConstants.h new file mode 100644 index 0000000000..642a794491 --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRConstants.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OCULUSVRCONSTANTS_H_ +#define _OCULUSVRCONSTANTS_H_ + +namespace OculusVRConstants +{ + enum Constants { + DefaultOVRBase = 0, + MaxSensors = 1, + }; +} + +#endif // _OCULUSVRCONSTANTS_H_ diff --git a/Engine/source/platform/input/oculusVR/oculusVRDevice.cpp b/Engine/source/platform/input/oculusVR/oculusVRDevice.cpp new file mode 100644 index 0000000000..c6dd0513a6 --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRDevice.cpp @@ -0,0 +1,844 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/input/oculusVR/oculusVRDevice.h" +#include "platform/platformInput.h" +#include "core/module.h" +#include "console/engineAPI.h" +#include "T3D/gameBase/gameConnection.h" + +MODULE_BEGIN( OculusVRDevice ) + + MODULE_INIT_AFTER( InputEventManager ) + MODULE_SHUTDOWN_BEFORE( InputEventManager ) + + MODULE_INIT + { + OculusVRDevice::staticInit(); + ManagedSingleton< OculusVRDevice >::createSingleton(); + if(OculusVRDevice::smEnableDevice) + { + OCULUSVRDEV->enable(); + } + + // Register the device with the Input Event Manager + INPUTMGR->registerDevice(OCULUSVRDEV); + } + + MODULE_SHUTDOWN + { + INPUTMGR->unregisterDevice(OCULUSVRDEV); + ManagedSingleton< OculusVRDevice >::deleteSingleton(); + } + +MODULE_END; + +//----------------------------------------------------------------------------- +// OculusVRDevice +//----------------------------------------------------------------------------- + +bool OculusVRDevice::smEnableDevice = true; + +bool OculusVRDevice::smSimulateHMD = true; + +bool OculusVRDevice::smGenerateAngleAxisRotationEvents = true; +bool OculusVRDevice::smGenerateEulerRotationEvents = false; + +bool OculusVRDevice::smGenerateRotationAsAxisEvents = false; +F32 OculusVRDevice::smMaximumAxisAngle = 25.0f; + +bool OculusVRDevice::smGenerateWholeFrameEvents = false; + +OculusVRDevice::OculusVRDevice() +{ + // From IInputDevice + dStrcpy(mName, "oculusvr"); + mDeviceType = INPUTMGR->getNextDeviceType(); + + // + mEnabled = false; + mActive = false; + + // We don't current support scaling of the input texture. The graphics pipeline will + // need to be modified for this. + mScaleInputTexture = false; + + mDeviceManager = NULL; + mListener = NULL; + + buildCodeTable(); +} + +OculusVRDevice::~OculusVRDevice() +{ + cleanUp(); +} + +void OculusVRDevice::staticInit() +{ + Con::addVariable("pref::OculusVR::EnableDevice", TypeBool, &smEnableDevice, + "@brief If true, the Oculus VR device will be enabled, if present.\n\n" + "@ingroup Game"); + + Con::addVariable("OculusVR::GenerateAngleAxisRotationEvents", TypeBool, &smGenerateAngleAxisRotationEvents, + "@brief If true, broadcast sensor rotation events as angled axis.\n\n" + "@ingroup Game"); + Con::addVariable("OculusVR::GenerateEulerRotationEvents", TypeBool, &smGenerateEulerRotationEvents, + "@brief If true, broadcast sensor rotation events as Euler angles about the X, Y and Z axis.\n\n" + "@ingroup Game"); + + Con::addVariable("OculusVR::GenerateRotationAsAxisEvents", TypeBool, &smGenerateRotationAsAxisEvents, + "@brief If true, broadcast sensor rotation as axis events.\n\n" + "@ingroup Game"); + Con::addVariable("OculusVR::MaximumAxisAngle", TypeF32, &smMaximumAxisAngle, + "@brief The maximum sensor angle when used as an axis event as measured from a vector pointing straight up (in degrees).\n\n" + "Should range from 0 to 90 degrees.\n\n" + "@ingroup Game"); + + Con::addVariable("OculusVR::GenerateWholeFrameEvents", TypeBool, &smGenerateWholeFrameEvents, + "@brief Indicates that a whole frame event should be generated and frames should be buffered.\n\n" + "@ingroup Game"); +} + +void OculusVRDevice::cleanUp() +{ + disable(); +} + +void OculusVRDevice::buildCodeTable() +{ + // Build the sensor device code table + OculusVRSensorDevice::buildCodeTable(); +} + +void OculusVRDevice::addHMDDevice(OVR::HMDDevice* hmd) +{ + if(!hmd) + return; + + OVR::HMDInfo hmdInfo; + if(!hmd->GetDeviceInfo(&hmdInfo)) + return; + + OculusVRHMDDevice* hmdd = new OculusVRHMDDevice(); + hmdd->set(hmd, hmdInfo, mScaleInputTexture); + mHMDDevices.push_back(hmdd); + + Con::printf(" HMD found: %s by %s [v%d]", hmdInfo.ProductName, hmdInfo.Manufacturer, hmdInfo.Version); +} + +void OculusVRDevice::createSimulatedHMD() +{ + OculusVRHMDDevice* hmdd = new OculusVRHMDDevice(); + hmdd->createSimulation(OculusVRHMDDevice::ST_RIFT_PREVIEW, mScaleInputTexture); + mHMDDevices.push_back(hmdd); + + Con::printf(" HMD simulated: %s by %s [v%d]", hmdd->getProductName(), hmdd->getManufacturer(), hmdd->getVersion()); +} + +void OculusVRDevice::addSensorDevice(OVR::SensorDevice* sensor) +{ + if(!sensor) + return; + + OVR::SensorInfo sensorInfo; + if(!sensor->GetDeviceInfo(&sensorInfo)) + return; + + OculusVRSensorDevice* sensord = new OculusVRSensorDevice(); + sensord->set(sensor, sensorInfo, mSensorDevices.size()); + mSensorDevices.push_back(sensord); + + Con::printf(" Sensor found: %s by %s [v%d] %s", sensorInfo.ProductName, sensorInfo.Manufacturer, sensorInfo.Version, sensorInfo.SerialNumber); +} + +void OculusVRDevice::createSimulatedSensor() +{ + OculusVRSensorDevice* sensord = new OculusVRSensorDevice(); + sensord->createSimulation(OculusVRSensorDevice::ST_RIFT_PREVIEW, mSensorDevices.size()); + mSensorDevices.push_back(sensord); + + Con::printf(" Sensor simulated: %s by %s [v%d] %s", sensord->getProductName(), sensord->getManufacturer(), sensord->getVersion(), sensord->getSerialNumber()); +} + +bool OculusVRDevice::enable() +{ + // Start off with disabling the device if it is already enabled + disable(); + + Con::printf("Oculus VR Device Init:"); + + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); + if(OVR::System::IsInitialized()) + { + mEnabled = true; + + // Create the OVR device manager + mDeviceManager = OVR::DeviceManager::Create(); + if(!mDeviceManager) + { + if(smSimulateHMD) + { + Con::printf(" Could not create a HMD device manager. Simulating a HMD."); + Con::printf(" "); + + createSimulatedHMD(); + createSimulatedSensor(); + setActive(true); + return true; + } + else + { + Con::printf(" Could not create a HMD device manager."); + Con::printf(" "); + + mEnabled = false; + OVR::System::Destroy(); + return false; + } + } + + // Provide a message listener + // NOTE: Commented out as non-functional in 0.1.2 + //mListener = new DeviceListener(this); + //mDeviceManager->SetMessageHandler(mListener); + + // Enumerate HMDs and pick the first one + OVR::HMDDevice* hmd = mDeviceManager->EnumerateDevices().CreateDevice(); + if(hmd) + { + // Add the HMD to our list + addHMDDevice(hmd); + + // Detect and add any sensor on the HMD + OVR::SensorDevice* sensor = hmd->GetSensor(); + if(sensor) + { + addSensorDevice(sensor); + } + else + { + Con::printf(" No sensor device on HMD."); + } + + setActive(true); + } + else + { + if(smSimulateHMD) + { + Con::printf(" Could not enumerate a HMD device. Simulating a HMD."); + createSimulatedHMD(); + createSimulatedSensor(); + setActive(true); + } + else + { + Con::printf(" Could not enumerate a HMD device."); + } + } + + } + + Con::printf(" "); + + return false; +} + +void OculusVRDevice::disable() +{ + for(U32 i=0; iRelease(); + mDeviceManager = NULL; + } + + if(mEnabled) + { + OVR::System::Destroy(); + } + + if(mListener) + { + delete mListener; + mListener = NULL; + } + + setActive(false); + mEnabled = false; +} + +bool OculusVRDevice::process() +{ + if(!mEnabled) + return false; + + if(!getActive()) + return false; + + //Build the maximum axis angle to be passed into the sensor process() + F32 maxAxisRadius = mSin(mDegToRad(smMaximumAxisAngle)); + + // Process each sensor + for(U32 i=0; iprocess(mDeviceType, smGenerateAngleAxisRotationEvents, smGenerateEulerRotationEvents, smGenerateRotationAsAxisEvents, maxAxisRadius); + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool OculusVRDevice::providesYFOV() const +{ + if(!mHMDDevices.size()) + return false; + + return true; +} + +F32 OculusVRDevice::getYFOV() const +{ + if(!mHMDDevices.size()) + return 0.0f; + + const OculusVRHMDDevice* hmd = getHMDDevice(0); + if(!hmd) + return 0.0f; + + return hmd->getYFOV(); +} + +bool OculusVRDevice::providesEyeOffset() const +{ + if(!mHMDDevices.size()) + return false; + + return true; +} + +const Point3F& OculusVRDevice::getEyeOffset() const +{ + if(!mHMDDevices.size()) + return Point3F::Zero; + + const OculusVRHMDDevice* hmd = getHMDDevice(0); + if(!hmd) + return Point3F::Zero; + + return hmd->getEyeWorldOffset(); +} + +bool OculusVRDevice::providesProjectionOffset() const +{ + if(!mHMDDevices.size()) + return false; + + return true; +} + +const Point2F& OculusVRDevice::getProjectionOffset() const +{ + if(!mHMDDevices.size()) + return Point2F::Zero; + + const OculusVRHMDDevice* hmd = getHMDDevice(0); + if(!hmd) + return Point2F::Zero; + + return hmd->getProjectionCenterOffset(); +} + +//----------------------------------------------------------------------------- + +const OculusVRHMDDevice* OculusVRDevice::getHMDDevice(U32 index) const +{ + if(index >= mHMDDevices.size()) + return NULL; + + return mHMDDevices[index]; +} + +//----------------------------------------------------------------------------- + +const OculusVRSensorDevice* OculusVRDevice::getSensorDevice(U32 index) const +{ + if(index >= mSensorDevices.size()) + return NULL; + + return mSensorDevices[index]; +} + +EulerF OculusVRDevice::getSensorEulerRotation(U32 index) +{ + if(index >= mSensorDevices.size()) + return Point3F::Zero; + + return mSensorDevices[index]->getEulerRotation(); +} + +F32 OculusVRDevice::getSensorPredictionTime(U32 index) +{ + const OculusVRSensorDevice* sensor = getSensorDevice(index); + if(!sensor || !sensor->isValid()) + return 0.0f; + + return sensor->getPredictionTime(); +} + +void OculusVRDevice::setSensorPredictionTime(U32 index, F32 dt) +{ + if(index >= mSensorDevices.size()) + return; + + OculusVRSensorDevice* sensor = mSensorDevices[index]; + if(!sensor->isValid()) + return; + + sensor->setPredictionTime(dt); +} + +void OculusVRDevice::setAllSensorPredictionTime(F32 dt) +{ + for(U32 i=0; isetPredictionTime(dt); + } +} + +void OculusVRDevice::resetAllSensors() +{ + // Reset each sensor + for(U32 i=0; ireset(); + } +} + +//----------------------------------------------------------------------------- + +void OculusVRDevice::DeviceListener::OnMessage(const OVR::Message& msg) +{ + switch(msg.Type) + { + case OVR::Message_DeviceAdded: + { + const OVR::MessageDeviceStatus* status = static_cast(&msg); + Con::printf("OVR: Device added of type: %d", status->Handle.GetType()); + } + break; + + case OVR::Message_DeviceRemoved: + Con::printf("OVR: Device removed of type: %d", msg.pDevice->GetType()); + break; + + default: + break; + } +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction(isOculusVRDeviceActive, bool, (),, + "@brief Used to determine if the Oculus VR input device is active\n\n" + + "The Oculus VR device is considered active when the library has been " + "initialized and either a real of simulated HMD is present.\n\n" + + "@return True if the Oculus VR input device is active.\n" + + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return false; + } + + return OCULUSVRDEV->getActive(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction(setOVRHMDAsGameConnectionDisplayDevice, bool, (GameConnection* conn),, + "@brief Sets the first HMD to be a GameConnection's display device\n\n" + "@param conn The GameConnection to set.\n" + "@return True if the GameConnection display device was set.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + Con::errorf("setOVRHMDAsGameConnectionDisplayDevice(): No Oculus VR Device present."); + return false; + } + + if(!conn) + { + Con::errorf("setOVRHMDAsGameConnectionDisplayDevice(): Invalid GameConnection."); + return false; + } + + conn->setDisplayDevice(OCULUSVRDEV); + return true; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction(getOVRHMDCount, S32, (),, + "@brief Get the number of HMD devices that are currently connected.\n\n" + "@return The number of Oculus VR HMD devices that are currently connected.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return 0; + } + + return OCULUSVRDEV->getHMDCount(); +} + +DefineEngineFunction(isOVRHMDSimulated, bool, (S32 index),, + "@brief Determines if the requested OVR HMD is simulated or real.\n\n" + "@param index The HMD index.\n" + "@return True if the HMD is simulated.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return true; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return true; + } + + return hmd->isSimulated(); +} + +DefineEngineFunction(getOVRHMDProductName, const char*, (S32 index),, + "@brief Retrieves the HMD product name.\n\n" + "@param index The HMD index.\n" + "@return The name of the HMD product.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return ""; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return ""; + } + + return hmd->getProductName(); +} + +DefineEngineFunction(getOVRHMDManufacturer, const char*, (S32 index),, + "@brief Retrieves the HMD manufacturer name.\n\n" + "@param index The HMD index.\n" + "@return The manufacturer of the HMD product.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return ""; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return ""; + } + + return hmd->getManufacturer(); +} + +DefineEngineFunction(getOVRHMDVersion, S32, (S32 index),, + "@brief Retrieves the HMD version number.\n\n" + "@param index The HMD index.\n" + "@return The version number of the HMD product.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return -1; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return -1; + } + + return hmd->getVersion(); +} + +DefineEngineFunction(getOVRHMDDisplayDeviceName, const char*, (S32 index),, + "@brief Windows display device name used in EnumDisplaySettings/CreateDC.\n\n" + "@param index The HMD index.\n" + "@return The name of the HMD display device, if any.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return ""; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return ""; + } + + return hmd->getDisplayDeviceName(); +} + +DefineEngineFunction(getOVRHMDResolution, Point2I, (S32 index),, + "@brief Provides the OVR HMD screen resolution.\n\n" + "@param index The HMD index.\n" + "@return A two component string with the screen's resolution.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return Point2I(1280, 800); + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return Point2I(1280, 800); + } + + return hmd->getResolution(); +} + +DefineEngineFunction(getOVRHMDDistortionCoefficients, String, (S32 index),, + "@brief Provides the OVR HMD distortion coefficients.\n\n" + "@param index The HMD index.\n" + "@return A four component string with the distortion coefficients.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return "0 0 0 0"; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return "0 0 0 0"; + } + + const Point4F& k = hmd->getKDistortion(); + char buf[256]; + dSprintf(buf, 256, "%g %g %g %g", k.x, k.y, k.z, k.w); + + return buf; +} + +DefineEngineFunction(getOVRHMDEyeXOffsets, Point2F, (S32 index),, + "@brief Provides the OVR HMD eye x offsets in uv coordinates.\n\n" + "@param index The HMD index.\n" + "@return A two component string with the left and right eye x offsets.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return Point2F(0.5, 0.5); + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return Point2F(0.5, 0.5); + } + + // X component is left, Y component is right + const Point2F& offset = hmd->getEyeUVOffset(); + return offset; +} + +DefineEngineFunction(getOVRHMDXCenterOffset, F32, (S32 index),, + "@brief Provides the OVR HMD calculated XCenterOffset.\n\n" + "@param index The HMD index.\n" + "@return The calculated XCenterOffset.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return 0.0f; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return 0.0f; + } + + F32 offset = hmd->getCenterOffset(); + return offset; +} + +DefineEngineFunction(getOVRHMDDistortionScale, F32, (S32 index),, + "@brief Provides the OVR HMD calculated distortion scale.\n\n" + "@param index The HMD index.\n" + "@return The calculated distortion scale.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return 1.0f; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return 1.0f; + } + + F32 scale = hmd->getDistortionScale(); + return scale; +} + +DefineEngineFunction(getOVRHMDYFOV, F32, (S32 index),, + "@brief Provides the OVR HMD calculated Y FOV.\n\n" + "@param index The HMD index.\n" + "@return The calculated Y FOV.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return 1.0f; + } + + const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); + if(!hmd) + { + return 1.0f; + } + + F32 fov = hmd->getYFOV(); + return mRadToDeg(fov); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction(getOVRSensorCount, S32, (),, + "@brief Get the number of sensor devices that are currently connected.\n\n" + "@return The number of Oculus VR sensor devices that are currently connected.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return 0; + } + + return OCULUSVRDEV->getSensorCount(); +} + +DefineEngineFunction(getOVRSensorEulerRotation, Point3F, (S32 index),, + "@brief Get the Euler rotation values for the given sensor index.\n\n" + "@param index The sensor index.\n" + "@return The Euler rotation values of the Oculus VR sensor, in degrees.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return Point3F::Zero; + } + + EulerF rot = OCULUSVRDEV->getSensorEulerRotation(index); + return Point3F(mRadToDeg(rot.x), mRadToDeg(rot.y), mRadToDeg(rot.z)); +} + +DefineEngineFunction(getOVRSensorPredictionTime, F32, (S32 index),, + "@brief Get the prediction time set for the given sensor index.\n\n" + "@param index The sensor index.\n" + "@return The prediction time of the Oculus VR sensor, given in seconds.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return 0; + } + + return OCULUSVRDEV->getSensorPredictionTime(index); +} + +DefineEngineFunction(setSensorPredictionTime, void, (S32 index, F32 dt),, + "@brief Set the prediction time set for the given sensor index.\n\n" + "@param index The sensor index.\n" + "@param dt The prediction time to set given in seconds. Setting to 0 disables prediction.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return; + } + + OCULUSVRDEV->setSensorPredictionTime(index, dt); +} + +DefineEngineFunction(setAllSensorPredictionTime, void, (F32 dt),, + "@brief Set the prediction time set for all sensors.\n\n" + "@param dt The prediction time to set given in seconds. Setting to 0 disables prediction.\n" + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return; + } + + OCULUSVRDEV->setAllSensorPredictionTime(dt); +} + +DefineEngineFunction(ovrResetAllSensors, void, (),, + "@brief Resets all Oculus VR sensors.\n\n" + "This resets all sensor orientations such that their 'normal' rotation " + "is defined when this function is called. This defines an HMD's forwards " + "and up direction, for example." + "@ingroup Game") +{ + if(!ManagedSingleton::instanceOrNull()) + { + return; + } + + OCULUSVRDEV->resetAllSensors(); +} diff --git a/Engine/source/platform/input/oculusVR/oculusVRDevice.h b/Engine/source/platform/input/oculusVR/oculusVRDevice.h new file mode 100644 index 0000000000..4ca37a6cc7 --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRDevice.h @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OCULUSVRDEVICE_H_ +#define _OCULUSVRDEVICE_H_ + +#include "platform/input/oculusVR/oculusVRConstants.h" +#include "platform/input/oculusVR/oculusVRHMDDevice.h" +#include "platform/input/oculusVR/oculusVRSensorDevice.h" +#include "platform/input/IInputDevice.h" +#include "platform/input/event.h" +#include "platform/output/IDisplayDevice.h" +#include "core/util/tSingleton.h" +#include "math/mQuat.h" +#include "math/mPoint4.h" +#include "OVR.h" + +#define DEFAULT_RIFT_UNIT 0 + +class OculusVRDevice : public IInputDevice, public IDisplayDevice +{ +public: + static bool smEnableDevice; + + // If no HMD is present simulate it being available + static bool smSimulateHMD; + + // Type of rotation events to broadcast + static bool smGenerateAngleAxisRotationEvents; + static bool smGenerateEulerRotationEvents; + + // Broadcast sensor rotation as axis + static bool smGenerateRotationAsAxisEvents; + + // The maximum sensor angle when used as an axis event + // as measured from a vector pointing straight up (in degrees) + static F32 smMaximumAxisAngle; + + // Indicates that a whole frame event should be generated and frames + // should be buffered. + static bool smGenerateWholeFrameEvents; + +protected: + class DeviceListener : public OVR::MessageHandler + { + protected: + OculusVRDevice* mOwner; + + public: + DeviceListener(OculusVRDevice* owner) { mOwner = owner; } + virtual ~DeviceListener() { mOwner = NULL; } + + virtual void OnMessage(const OVR::Message&); + }; + + // Our OVR SDK device listener class + DeviceListener* mListener; + + // The OVR SDK device manager + OVR::DeviceManager* mDeviceManager; + + // Discovered HMD devices + Vector mHMDDevices; + + // Discovered sensor devices + Vector mSensorDevices; + + /// Is the device active + bool mActive; + + // Should the input texture into the HMD (the render target that the scene has been + // rendered to) be scaled according to the HMD's distortion calculation? + bool mScaleInputTexture; + +protected: + void cleanUp(); + + /// Build out the codes used for controller actions with the + /// Input Event Manager + void buildCodeTable(); + + void addHMDDevice(OVR::HMDDevice* hmd); + + void createSimulatedHMD(); + + void addSensorDevice(OVR::SensorDevice* sensor); + + void createSimulatedSensor(); + +public: + OculusVRDevice(); + ~OculusVRDevice(); + + static void staticInit(); + + bool enable(); + void disable(); + + bool getActive() { return mActive; } + void setActive(bool state) { mActive = state; } + + bool process(); + + // IDisplayDevice + virtual bool providesYFOV() const; + virtual F32 getYFOV() const; + virtual bool providesEyeOffset() const; + virtual const Point3F& getEyeOffset() const; + virtual bool providesProjectionOffset() const; + virtual const Point2F& getProjectionOffset() const; + + // HMDs + U32 getHMDCount() const { return mHMDDevices.size(); } + const OculusVRHMDDevice* getHMDDevice(U32 index) const; + + // Sensors + U32 getSensorCount() const { return mSensorDevices.size(); } + const OculusVRSensorDevice* getSensorDevice(U32 index) const; + EulerF getSensorEulerRotation(U32 index); + F32 getSensorPredictionTime(U32 index); + void setSensorPredictionTime(U32 index, F32 dt); + void setAllSensorPredictionTime(F32 dt); + void resetAllSensors(); + +public: + // For ManagedSingleton. + static const char* getSingletonName() { return "OculusVRDevice"; } +}; + +/// Returns the OculusVRDevice singleton. +#define OCULUSVRDEV ManagedSingleton::instance() + +#endif // _OCULUSVRDEVICE_H_ diff --git a/Engine/source/platform/input/oculusVR/oculusVRHMDDevice.cpp b/Engine/source/platform/input/oculusVR/oculusVRHMDDevice.cpp new file mode 100644 index 0000000000..96919ff81d --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRHMDDevice.cpp @@ -0,0 +1,208 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/input/oculusVR/oculusVRHMDDevice.h" + +OculusVRHMDDevice::OculusVRHMDDevice() +{ + mIsValid = false; + mIsSimulation = false; + mDevice = NULL; +} + +OculusVRHMDDevice::~OculusVRHMDDevice() +{ + cleanUp(); +} + +void OculusVRHMDDevice::cleanUp() +{ + if(mDevice) + { + mDevice->Release(); + mDevice = NULL; + } + + mIsValid = false; +} + +void OculusVRHMDDevice::set(OVR::HMDDevice* hmd, OVR::HMDInfo& info, bool calculateDistortionScale) +{ + mIsValid = false; + mIsSimulation = false; + + mDevice = hmd; + + // DeviceInfo + mProductName = info.ProductName; + mManufacturer = info.Manufacturer; + mVersion = info.Version; + + mDisplayDeviceName = info.DisplayDeviceName; + + mResolution.x = info.HResolution; + mResolution.y = info.VResolution; + + mScreenSize.x = info.HScreenSize; + mScreenSize.y = info.VScreenSize; + + mVerticalEyeCenter = info.VScreenCenter; + mEyeToScreen = info.EyeToScreenDistance; + mLensSeparation = info.LensSeparationDistance; + mInterpupillaryDistance = info.InterpupillaryDistance; + + mKDistortion.x = info.DistortionK[0]; + mKDistortion.y = info.DistortionK[1]; + mKDistortion.z = info.DistortionK[2]; + mKDistortion.w = info.DistortionK[3]; + + // Calculated values + calculateValues(calculateDistortionScale); + + mIsValid = true; +} + +void OculusVRHMDDevice::createSimulation(SimulationTypes simulationType, bool calculateDistortionScale) +{ + if(simulationType == ST_RIFT_PREVIEW) + { + createSimulatedPreviewRift(calculateDistortionScale); + } +} + +void OculusVRHMDDevice::createSimulatedPreviewRift(bool calculateDistortionScale) +{ + mIsValid = true; + mIsSimulation = true; + + mProductName = "Oculus Rift DK1-SLA1"; + mManufacturer = "Oculus VR"; + mVersion = 0; + + mDisplayDeviceName = ""; + + mResolution.x = 1280; + mResolution.y = 800; + + mScreenSize.x = 0.14975999f; + mScreenSize.y = 0.093599997f; + + mVerticalEyeCenter = 0.046799999f; + mEyeToScreen = 0.041000001f; + mLensSeparation = 0.064000003f; + mInterpupillaryDistance = 0.064000003f; + + mKDistortion.x = 1.0000000f; + mKDistortion.y = 0.22000000f; + mKDistortion.z = 0.23999999f; + mKDistortion.w = 0.00000000f; + + calculateValues(calculateDistortionScale); +} + +// Computes scale that should be applied to the input render texture +// before distortion to fit the result in the same screen size. +// The 'fitRadius' parameter specifies the distance away from distortion center at +// which the input and output coordinates will match, assuming [-1,1] range. +F32 OculusVRHMDDevice::calcScale(F32 fitRadius) +{ + F32 s = fitRadius; + + // This should match distortion equation used in shader. + F32 ssq = s * s; + F32 scale = s * (mKDistortion.x + mKDistortion.y * ssq + mKDistortion.z * ssq * ssq + mKDistortion.w * ssq * ssq * ssq); + return scale; +} + +void OculusVRHMDDevice::calculateValues(bool calculateDistortionScale) +{ + F32 halfScreenX = mScreenSize.x * 0.5f; + if(halfScreenX > 0) + { + F32 halfLensSeparation = mLensSeparation * 0.5; + F32 offset = halfLensSeparation / halfScreenX; + mEyeUVOffset.x = offset - 0.5; + mEyeUVOffset.y = 1.0f - offset - 0.5; + } + else + { + mEyeUVOffset.x = 0.5f; + mEyeUVOffset.y = 0.5f; + } + + F32 lensOffset = mLensSeparation * 0.5f; + F32 lensShift = mScreenSize.x * 0.25f - lensOffset; + F32 lensViewportShift = 4.0f * lensShift / mScreenSize.x; + mXCenterOffset= lensViewportShift; + + // Determine how the input texture should be scaled relative to the back buffer + // so that we fit the distorted view to the backbuffer after calculating the + // distortion. In reference to section 5.6.3 Distortion Scale and FOV in the + // SDK docs. + if(!calculateDistortionScale) + { + // Do not calculate a distortion scale for the input texture. This means that the input + // texture and the backbuffer will be the same resolution. + mDistortionFit.x = 0.0f; + mDistortionFit.y = 0.0f; + } + else if (mScreenSize.x > 0.140f) // 7" + { + mDistortionFit.x = -1.0f; + mDistortionFit.y = 0.0f; + } + else // 5" + { + mDistortionFit.x = 0.0f; + mDistortionFit.y = 1.0f; + } + + // Compute distortion scale from DistortionFitX & DistortionFitY. + // Fit value of 0.0 means "no fit". + if (mIsZero(mDistortionFit.x) && mIsZero(mDistortionFit.y)) + { + mDistortionScale = 1.0f; + } + else + { + // Convert fit value to distortion-centered coordinates before fit radius + // calculation. + // NOTE: For now just assume a full view the same size as the HMD supports. It is + // possible that this full view is smaller or larger. + F32 stereoAspect = 0.5f * mResolution.x / mResolution.y; + F32 dx = mDistortionFit.x - mXCenterOffset; + F32 dy = mDistortionFit.y / stereoAspect; + F32 fitRadius = sqrt(dx * dx + dy * dy); + mDistortionScale = calcScale(fitRadius)/fitRadius; + } + + // Calculate the vertical FOV for a single eye + mAspectRatio = F32(mResolution.x * 0.5f) / F32(mResolution.y); + F32 halfScreenDistance = mScreenSize.y * 0.5f * mDistortionScale; + mYFOV = 2.0f * mAtan(halfScreenDistance / mEyeToScreen); + + F32 viewCenter = mScreenSize.x * 0.25f; + F32 eyeProjectionShift = viewCenter - (mInterpupillaryDistance * 0.5f); + mProjectionCenterOffset.set(4.0f * eyeProjectionShift / mScreenSize.x, 0.0f); + + mEyeWorldOffset.set(mInterpupillaryDistance * 0.5f, 0.0f, 0.0f); +} diff --git a/Engine/source/platform/input/oculusVR/oculusVRHMDDevice.h b/Engine/source/platform/input/oculusVR/oculusVRHMDDevice.h new file mode 100644 index 0000000000..dce8234eb7 --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRHMDDevice.h @@ -0,0 +1,187 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OCULUSVRHMDDEVICE_H_ +#define _OCULUSVRHMDDEVICE_H_ + +#include "core/util/str.h" +#include "math/mQuat.h" +#include "math/mPoint2.h" +#include "math/mPoint3.h" +#include "math/mPoint4.h" +#include "platform/input/oculusVR/oculusVRConstants.h" +#include "platform/types.h" +#include "OVR.h" + +class OculusVRHMDDevice +{ +public: + enum SimulationTypes { + ST_RIFT_PREVIEW, + }; + +protected: + bool mIsValid; + + bool mIsSimulation; + + OVR::HMDDevice* mDevice; + + // From OVR::DeviceInfo + String mProductName; + String mManufacturer; + U32 mVersion; + + // Windows display device name used in EnumDisplaySettings/CreateDC + String mDisplayDeviceName; + + // Whole screen resolution + Point2I mResolution; + + // Physical screen size in meters + Point2F mScreenSize; + + // Physical offset from the top of the screen to the center of the + // eye, in meters. Usually half of the vertical physical screen size + F32 mVerticalEyeCenter; + + // Physical distance from the eye to the screen + F32 mEyeToScreen; + + // Physical distance between lens centers, in meters + F32 mLensSeparation; + + // Physical distance between the user's eye centers + F32 mInterpupillaryDistance; + + // The eye IPD as a Point3F + Point3F mEyeWorldOffset; + + // Radial distortion correction coefficients used by the barrel distortion shader + Point4F mKDistortion; + + // Calculated values of eye x offset from center in normalized (uv) coordinates + // where each eye is 0..1. Used for the mono to stereo postFX to simulate an + // eye offset of the camera. The x component is the left eye, the y component + // is the right eye. + Point2F mEyeUVOffset; + + // Used to adjust where an eye's view is rendered to account for the lenses not + // being in the center of the physical screen half. + F32 mXCenterOffset; + + // When calculating the distortion scale to use to increase the size of the input texture + // this determines how we should attempt to fit the distorted view into the backbuffer. + Point2F mDistortionFit; + + // Is the factor by which the input texture size is increased to make post-distortion + // result distortion fit the viewport. If the input texture is the same size as the + // backbuffer, then this should be 1.0. + F32 mDistortionScale; + + // Aspect ratio for a single eye + F32 mAspectRatio; + + // Vertical field of view + F32 mYFOV; + + // The amount to offset the projection matrix to account for the eye not being in the + // center of the screen. + Point2F mProjectionCenterOffset; + +protected: + F32 calcScale(F32 fitRadius); + + void calculateValues(bool calculateDistortionScale); + + void createSimulatedPreviewRift(bool calculateDistortionScale); + +public: + OculusVRHMDDevice(); + ~OculusVRHMDDevice(); + + void cleanUp(); + + // Set the HMD properties based on information from the OVR device + void set(OVR::HMDDevice* hmd, OVR::HMDInfo& info, bool calculateDistortionScale); + + // Set the HMD properties based on a simulation of the given type + void createSimulation(SimulationTypes simulationType, bool calculateDistortionScale); + + bool isValid() const {return mIsValid;} + bool isSimulated() const {return mIsSimulation;} + + const char* getProductName() const { return mProductName.c_str(); } + const char* getManufacturer() const { return mManufacturer.c_str(); } + U32 getVersion() const { return mVersion; } + + // Windows display device name used in EnumDisplaySettings/CreateDC + const char* getDisplayDeviceName() const { return mDisplayDeviceName.c_str(); } + + // Whole screen resolution + const Point2I& getResolution() const { return mResolution; } + + // Physical screen size in meters + const Point2F& getScreenSize() const { return mScreenSize; } + + // Physical offset from the top of the screen to the center of the + // eye, in meters. Usually half of the vertical physical screen size + F32 getVerticalEyeCenter() const { return mVerticalEyeCenter; } + + // Physical distance from the eye to the screen + F32 getEyeToScreen() const { return mEyeToScreen; } + + // Physical distance between lens centers, in meters + F32 getLensSeparation() const { return mLensSeparation; } + + // Physical distance between the user's eye centers + F32 getIPD() const { return mInterpupillaryDistance; } + + // Provides the IPD of one eye as a Point3F + const Point3F& getEyeWorldOffset() const { return mEyeWorldOffset; } + + // Radial distortion correction coefficients used by the barrel distortion shader + const Point4F& getKDistortion() const { return mKDistortion; } + + // Calculated values of eye x offset from center in normalized (uv) coordinates. + const Point2F& getEyeUVOffset() const { return mEyeUVOffset; } + + // Used to adjust where an eye's view is rendered to account for the lenses not + // being in the center of the physical screen half. + F32 getCenterOffset() const { return mXCenterOffset; } + + // Is the factor by which the input texture size is increased to make post-distortion + // result distortion fit the viewport. + F32 getDistortionScale() const { return mDistortionScale; } + + // Aspect ration for a single eye + F32 getAspectRation() const { return mAspectRatio; } + + // Vertical field of view + F32 getYFOV() const { return mYFOV; } + + // The amount to offset the projection matrix to account for the eye not being in the + // center of the screen. + const Point2F& getProjectionCenterOffset() const { return mProjectionCenterOffset; } +}; + +#endif // _OCULUSVRHMDDEVICE_H_ diff --git a/Engine/source/platform/input/oculusVR/oculusVRSensorData.cpp b/Engine/source/platform/input/oculusVR/oculusVRSensorData.cpp new file mode 100644 index 0000000000..8d3a44b802 --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRSensorData.cpp @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/input/oculusVR/oculusVRSensorData.h" +#include "platform/input/oculusVR/oculusVRUtil.h" +#include "console/console.h" + +OculusVRSensorData::OculusVRSensorData() +{ + reset(); +} + +void OculusVRSensorData::reset() +{ + mDataSet = false; +} + +void OculusVRSensorData::setData(const OVR::SensorFusion& data, const F32& maxAxisRadius) +{ + // Sensor rotation + OVR::Quatf orientation; + if(data.GetPredictionDelta() > 0) + { + orientation = data.GetPredictedOrientation(); + } + else + { + orientation = data.GetOrientation(); + } + OVR::Matrix4f orientMat(orientation); + OculusVRUtil::convertRotation(orientMat.M, mRot); + mRotQuat.set(mRot); + + // Sensor rotation in Euler format + OculusVRUtil::convertRotation(orientation, mRotEuler); + + // Sensor rotation as axis + OculusVRUtil::calculateAxisRotation(mRot, maxAxisRadius, mRotAxis); + + mDataSet = true; +} + +void OculusVRSensorData::simulateData(const F32& maxAxisRadius) +{ + // Sensor rotation + mRot.identity(); + mRotQuat.identity(); + mRotEuler.zero(); + + // Sensor rotation as axis + OculusVRUtil::calculateAxisRotation(mRot, maxAxisRadius, mRotAxis); + + mDataSet = true; +} + +U32 OculusVRSensorData::compare(OculusVRSensorData* other) +{ + S32 result = DIFF_NONE; + + // Check rotation + if(mRotEuler.x != other->mRotEuler.x || mRotEuler.y != other->mRotEuler.y || mRotEuler.z != other->mRotEuler.z || !mDataSet) + { + result |= DIFF_ROT; + } + + // Check rotation as axis + if(mRotAxis.x != other->mRotAxis.x || !mDataSet) + { + result |= DIFF_ROTAXISX; + } + if(mRotAxis.y != other->mRotAxis.y || !mDataSet) + { + result |= DIFF_ROTAXISY; + } + + return result; +} diff --git a/Engine/source/platform/input/oculusVR/oculusVRSensorData.h b/Engine/source/platform/input/oculusVR/oculusVRSensorData.h new file mode 100644 index 0000000000..8f73e4531e --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRSensorData.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OCULUSVRSENSORDATA_H_ +#define _OCULUSVRSENSORDATA_H_ + +#include "platform/types.h" +#include "math/mMatrix.h" +#include "math/mQuat.h" +#include "math/mPoint2.h" +#include "OVR.h" + +struct OculusVRSensorData +{ + enum DataDifferences { + DIFF_NONE = 0, + DIFF_ROT = (1<<0), + DIFF_ROTAXISX = (1<<1), + DIFF_ROTAXISY = (1<<2), + + DIFF_ROTAXIS = (DIFF_ROTAXISX | DIFF_ROTAXISY), + }; + + bool mDataSet; + + // Rotation + MatrixF mRot; + QuatF mRotQuat; + EulerF mRotEuler; + + // Controller rotation as axis x, y + Point2F mRotAxis; + + OculusVRSensorData(); + + /// Reset the data + void reset(); + + /// Set data based on given sensor fusion + void setData(const OVR::SensorFusion& data, const F32& maxAxisRadius); + + /// Simulate valid data + void simulateData(const F32& maxAxisRadius); + + /// Compare this data and given and return differences + U32 compare(OculusVRSensorData* other); +}; + +#endif // _OCULUSVRSENSORDATA_H_ diff --git a/Engine/source/platform/input/oculusVR/oculusVRSensorDevice.cpp b/Engine/source/platform/input/oculusVR/oculusVRSensorDevice.cpp new file mode 100644 index 0000000000..dad2bba20b --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRSensorDevice.cpp @@ -0,0 +1,265 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/input/oculusVR/oculusVRSensorDevice.h" +#include "platform/input/oculusVR/oculusVRSensorData.h" +#include "platform/input/oculusVR/oculusVRUtil.h" +#include "platform/platformInput.h" + +U32 OculusVRSensorDevice::OVR_SENSORROT[OculusVRConstants::MaxSensors] = {0}; +U32 OculusVRSensorDevice::OVR_SENSORROTANG[OculusVRConstants::MaxSensors] = {0}; +U32 OculusVRSensorDevice::OVR_SENSORROTAXISX[OculusVRConstants::MaxSensors] = {0}; +U32 OculusVRSensorDevice::OVR_SENSORROTAXISY[OculusVRConstants::MaxSensors] = {0}; + +OculusVRSensorDevice::OculusVRSensorDevice() +{ + mIsValid = false; + mIsSimulation = false; + mDevice = NULL; + + for(U32 i=0; i<2; ++i) + { + mDataBuffer[i] = new OculusVRSensorData(); + } + mPrevData = mDataBuffer[0]; +} + +OculusVRSensorDevice::~OculusVRSensorDevice() +{ + cleanUp(); + + for(U32 i=0; i<2; ++i) + { + delete mDataBuffer[i]; + mDataBuffer[i] = NULL; + } + mPrevData = NULL; +} + +void OculusVRSensorDevice::cleanUp() +{ + mSensorFusion.AttachToSensor(NULL); + + if(mDevice) + { + mDevice->Release(); + mDevice = NULL; + } + + mIsValid = false; +} + +void OculusVRSensorDevice::set(OVR::SensorDevice* sensor, OVR::SensorInfo& info, S32 actionCodeIndex) +{ + mIsValid = false; + + mDevice = sensor; + mSensorFusion.AttachToSensor(sensor); + + // DeviceInfo + mProductName = info.ProductName; + mManufacturer = info.Manufacturer; + mVersion = info.Version; + + // SensorInfo + mVendorId = info.VendorId; + mProductId = info.ProductId; + mSerialNumber = info.SerialNumber; + + mActionCodeIndex = actionCodeIndex; + + if(mActionCodeIndex >= OculusVRConstants::MaxSensors) + { + // Cannot declare more sensors than we are able to handle + mIsValid = false; + } + else + { + mIsValid = true; + } +} + +void OculusVRSensorDevice::createSimulation(SimulationTypes simulationType, S32 actionCodeIndex) +{ + if(simulationType == ST_RIFT_PREVIEW) + { + createSimulatedPreviewRift(actionCodeIndex); + } +} + +void OculusVRSensorDevice::createSimulatedPreviewRift(S32 actionCodeIndex) +{ + mIsValid = false; + mIsSimulation = true; + + // DeviceInfo + mProductName = "Tracker DK"; + mManufacturer = "Oculus VR, Inc."; + mVersion = 0; + + // SensorInfo + mVendorId = 10291; + mProductId = 1; + mSerialNumber = "000000000000"; + + mActionCodeIndex = actionCodeIndex; + + if(mActionCodeIndex >= OculusVRConstants::MaxSensors) + { + // Cannot declare more sensors than we are able to handle + mIsValid = false; + } + else + { + mIsValid = true; + } +} + +void OculusVRSensorDevice::buildCodeTable() +{ + // Obtain all of the device codes + for(U32 i=0; igetNextDeviceCode(); + + OVR_SENSORROTANG[i] = INPUTMGR->getNextDeviceCode(); + + OVR_SENSORROTAXISX[i] = INPUTMGR->getNextDeviceCode(); + OVR_SENSORROTAXISY[i] = INPUTMGR->getNextDeviceCode(); + } + + // Build out the virtual map + char buffer[64]; + for(U32 i=0; iaddVirtualMap( buffer, SI_ROT, OVR_SENSORROT[i] ); + + dSprintf(buffer, 64, "ovr_sensorrotang%d", i); + INPUTMGR->addVirtualMap( buffer, SI_ROT, OVR_SENSORROTANG[i] ); + + dSprintf(buffer, 64, "ovr_sensorrotaxisx%d", i); + INPUTMGR->addVirtualMap( buffer, SI_AXIS, OVR_SENSORROTAXISX[i] ); + dSprintf(buffer, 64, "ovr_sensorrotaxisy%d", i); + INPUTMGR->addVirtualMap( buffer, SI_AXIS, OVR_SENSORROTAXISY[i] ); + } +} + +bool OculusVRSensorDevice::process(U32 deviceType, bool generateRotAsAngAxis, bool generateRotAsEuler, bool generateRotationAsAxisEvents, F32 maxAxisRadius) +{ + if(!mIsValid) + return false; + + // Store the current data from the sensor and compare with previous data + U32 diff; + OculusVRSensorData* currentBuffer = (mPrevData == mDataBuffer[0]) ? mDataBuffer[1] : mDataBuffer[0]; + if(!mIsSimulation) + { + currentBuffer->setData(mSensorFusion, maxAxisRadius); + } + else + { + currentBuffer->simulateData(maxAxisRadius); + } + diff = mPrevData->compare(currentBuffer); + + // Update the previous data pointer. We do this here in case someone calls our + // console functions during one of the input events below. + mPrevData = currentBuffer; + + // Rotation event + if(diff & OculusVRSensorData::DIFF_ROT) + { + if(generateRotAsAngAxis) + { + INPUTMGR->buildInputEvent(deviceType, OculusVRConstants::DefaultOVRBase, SI_ROT, OVR_SENSORROT[mActionCodeIndex], SI_MOVE, currentBuffer->mRotQuat); + } + + if(generateRotAsEuler) + { + // Convert angles to degrees + VectorF angles; + for(U32 i=0; i<3; ++i) + { + angles[i] = mRadToDeg(currentBuffer->mRotEuler[i]); + } + INPUTMGR->buildInputEvent(deviceType, OculusVRConstants::DefaultOVRBase, SI_POS, OVR_SENSORROTANG[mActionCodeIndex], SI_MOVE, angles); + } + } + + // Rotation as axis event + if(generateRotationAsAxisEvents && diff & OculusVRSensorData::DIFF_ROTAXIS) + { + if(diff & OculusVRSensorData::DIFF_ROTAXISX) + INPUTMGR->buildInputEvent(deviceType, OculusVRConstants::DefaultOVRBase, SI_AXIS, OVR_SENSORROTAXISX[mActionCodeIndex], SI_MOVE, currentBuffer->mRotAxis.x); + if(diff & OculusVRSensorData::DIFF_ROTAXISY) + INPUTMGR->buildInputEvent(deviceType, OculusVRConstants::DefaultOVRBase, SI_AXIS, OVR_SENSORROTAXISY[mActionCodeIndex], SI_MOVE, currentBuffer->mRotAxis.y); + } + + return true; +} + +void OculusVRSensorDevice::reset() +{ + if(!mIsValid) + return; + + mSensorFusion.Reset(); +} + +F32 OculusVRSensorDevice::getPredictionTime() const +{ + if(!mIsValid) + return 0.0f; + + return mSensorFusion.GetPredictionDelta(); +} + +void OculusVRSensorDevice::setPredictionTime(F32 dt) +{ + if(!mIsValid) + return; + + mSensorFusion.SetPrediction(dt); +} + +EulerF OculusVRSensorDevice::getEulerRotation() +{ + if(!mIsValid) + return Point3F::Zero; + + OVR::Quatf orientation; + if(mSensorFusion.GetPredictionDelta() > 0) + { + orientation = mSensorFusion.GetPredictedOrientation(); + } + else + { + orientation = mSensorFusion.GetOrientation(); + } + + // Sensor rotation in Euler format + EulerF rot; + OculusVRUtil::convertRotation(orientation, rot); + + return rot; +} diff --git a/Engine/source/platform/input/oculusVR/oculusVRSensorDevice.h b/Engine/source/platform/input/oculusVR/oculusVRSensorDevice.h new file mode 100644 index 0000000000..c5ed30322b --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRSensorDevice.h @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OCULUSVRSENSORDEVICE_H_ +#define _OCULUSVRSENSORDEVICE_H_ + +#include "core/util/str.h" +#include "math/mQuat.h" +#include "math/mPoint2.h" +#include "math/mPoint3.h" +#include "math/mPoint4.h" +#include "platform/input/oculusVR/oculusVRConstants.h" +#include "platform/types.h" +#include "OVR.h" + +struct OculusVRSensorData; + +class OculusVRSensorDevice +{ +public: + enum SimulationTypes { + ST_RIFT_PREVIEW, + }; + +public: + // Action codes + static U32 OVR_SENSORROT[OculusVRConstants::MaxSensors]; // SI_ROT + + static U32 OVR_SENSORROTANG[OculusVRConstants::MaxSensors]; // SI_POS but is EulerF + + static U32 OVR_SENSORROTAXISX[OculusVRConstants::MaxSensors]; // SI_AXIS + static U32 OVR_SENSORROTAXISY[OculusVRConstants::MaxSensors]; + +protected: + bool mIsValid; + + bool mIsSimulation; + + OVR::SensorDevice* mDevice; + + OVR::SensorFusion mSensorFusion; + + // From OVR::DeviceInfo + String mProductName; + String mManufacturer; + U32 mVersion; + + // From OVR::SensorInfo + U16 mVendorId; + U16 mProductId; + String mSerialNumber; + + // Assigned by the OculusVRDevice + S32 mActionCodeIndex; + + // Buffers to store data for sensor + OculusVRSensorData* mDataBuffer[2]; + + // Points to the buffer that holds the previously collected data + // for the sensor + OculusVRSensorData* mPrevData; + +protected: + void createSimulatedPreviewRift(S32 actionCodeIndex); + +public: + OculusVRSensorDevice(); + virtual ~OculusVRSensorDevice(); + + static void buildCodeTable(); + + void cleanUp(); + + // Set the sensor properties based on information from the OVR device + void set(OVR::SensorDevice* sensor, OVR::SensorInfo& info, S32 actionCodeIndex); + + // Set the sensor properties based on a simulation of the given type + void createSimulation(SimulationTypes simulationType, S32 actionCodeIndex); + + bool isValid() const {return mIsValid;} + bool isSimulated() {return mIsSimulation;} + + bool process(U32 deviceType, bool generateRotAsAngAxis, bool generateRotAsEuler, bool generateRotationAsAxisEvents, F32 maxAxisRadius); + + void reset(); + + // Get the prediction time for the sensor fusion. The time is in seconds. + F32 getPredictionTime() const; + + // Set the prediction time for the sensor fusion. The time is in seconds. + void setPredictionTime(F32 dt); + + const char* getProductName() { return mProductName.c_str(); } + const char* getManufacturer() { return mManufacturer.c_str(); } + U32 getVersion() { return mVersion; } + U16 getVendorId() { return mVendorId; } + U16 getProductId() { return mProductId; } + const char* getSerialNumber() { return mSerialNumber; } + + EulerF getEulerRotation(); +}; + +#endif // _OCULUSVRSENSORDEVICE_H_ diff --git a/Engine/source/platform/input/oculusVR/oculusVRUtil.cpp b/Engine/source/platform/input/oculusVR/oculusVRUtil.cpp new file mode 100644 index 0000000000..46b4c7a34a --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRUtil.cpp @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/input/oculusVR/oculusVRUtil.h" + +namespace OculusVRUtil +{ + +void convertRotation(const F32 inRotMat[4][4], MatrixF& outRotation) +{ + // Set rotation. We need to convert from sensor coordinates to + // Torque coordinates. The sensor matrix is stored row-major. + // The conversion is: + // + // Sensor Torque + // a b c a b c a -c b + // d e f --> -g -h -i --> -g i -h + // g h i d e f d -f e + outRotation.setColumn(0, Point4F( inRotMat[0][0], -inRotMat[2][0], inRotMat[1][0], 0.0f)); + outRotation.setColumn(1, Point4F(-inRotMat[0][2], inRotMat[2][2], -inRotMat[1][2], 0.0f)); + outRotation.setColumn(2, Point4F( inRotMat[0][1], -inRotMat[2][1], inRotMat[1][1], 0.0f)); + outRotation.setPosition(Point3F::Zero); +} + +void convertRotation(OVR::Quatf& inRotation, EulerF& outRotation) +{ + F32 yaw, pitch, roll; + inRotation.GetEulerAngles(&yaw, &pitch, &roll); + outRotation.x = -pitch; + outRotation.y = roll; + outRotation.z = -yaw; +} + +void calculateAxisRotation(const MatrixF& inRotation, const F32& maxAxisRadius, Point2F& outRotation) +{ + const VectorF& controllerUp = inRotation.getUpVector(); + Point2F axis(0,0); + axis.x = controllerUp.x; + axis.y = controllerUp.y; + + // Limit the axis angle to that given to us + if(axis.len() > maxAxisRadius) + { + axis.normalize(maxAxisRadius); + } + + // Renormalize to the range of 0..1 + if(maxAxisRadius != 0.0f) + { + axis /= maxAxisRadius; + } + + outRotation.x = axis.x; + outRotation.y = axis.y; +} + +} \ No newline at end of file diff --git a/Engine/source/platform/input/oculusVR/oculusVRUtil.h b/Engine/source/platform/input/oculusVR/oculusVRUtil.h new file mode 100644 index 0000000000..f4088f3c14 --- /dev/null +++ b/Engine/source/platform/input/oculusVR/oculusVRUtil.h @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OCULUSVRUTIL_H_ +#define _OCULUSVRUTIL_H_ + +#include "math/mPoint2.h" +#include "math/mMatrix.h" +#include "OVR.h" + +namespace OculusVRUtil +{ + /// Convert an OVR sensor's rotation to a Torque 3D matrix + void convertRotation(const F32 inRotMat[4][4], MatrixF& outRotation); + + /// Convert an OVR sensor's rotation to Torque 3D Euler angles (in radians) + void convertRotation(OVR::Quatf& inRotation, EulerF& outRotation); + + /// Calcualte a sensor's rotation as if it were a thumb stick axis + void calculateAxisRotation(const MatrixF& inRotation, const F32& maxAxisRadius, Point2F& outRotation); +} + +#endif // _OCULUSVRUTIL_H_ diff --git a/Templates/Empty/game/core/scripts/client/core.cs b/Templates/Empty/game/core/scripts/client/core.cs index 2213d982a1..da26beeffd 100644 --- a/Templates/Empty/game/core/scripts/client/core.cs +++ b/Templates/Empty/game/core/scripts/client/core.cs @@ -54,6 +54,9 @@ function initializeCore() exec( "./audioStates.cs" ); exec( "./audioAmbiences.cs" ); + // Input devices + exec("~/scripts/client/oculusVR.cs"); + // Seed the random number generator. setRandomSeed(); diff --git a/Templates/Empty/game/core/scripts/client/oculusVR.cs b/Templates/Empty/game/core/scripts/client/oculusVR.cs new file mode 100644 index 0000000000..81015b40f3 --- /dev/null +++ b/Templates/Empty/game/core/scripts/client/oculusVR.cs @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Only load these functions if an Oculus VR device is present +if(!isFunction(isOculusVRDeviceActive)) + return; + +//----------------------------------------------------------------------------- + +function oculusSensorMetricsCallback() +{ + return " | OVR Sensor 0 |" @ + " rot: " @ getOVRSensorEulerRotation(0); +} + +//----------------------------------------------------------------------------- + +// Call this function from createCanvas() to have the Canvas attach itself +// to the Rift's display. The Canvas' window will still open on the primary +// display if that is different from the Rift, but it will move to the Rift +// when it goes full screen. If the Rift is not connected then nothing +// will happen. +function pointCanvasToOculusVRDisplay() +{ + $pref::Video::displayOutputDevice = getOVRHMDDisplayDeviceName(0); +} + +//----------------------------------------------------------------------------- + +// Call this function from GameConnection::initialControlSet() just before +// your "Canvas.setContent(PlayGui);" call, or at any time you wish to switch +// to a side-by-side rendering and the appropriate barrel distortion. This +// will turn on side-by-side rendering and tell the GameConnection to use the +// Rift as its display device. +// Parameters: +// %gameConnection - The client GameConnection instance +// %trueStereoRendering - If true will enable stereo rendering with an eye +// offset for each viewport. This will render each frame twice. If false +// then a pseudo stereo rendering is done with only a single render per frame. +function enableOculusVRDisplay(%gameConnection, %trueStereoRendering) +{ + setOVRHMDAsGameConnectionDisplayDevice(%gameConnection); + PlayGui.renderStyle = "stereo side by side"; + + if(%trueStereoRendering) + { + OVRBarrelDistortionPostFX.isEnabled = true; + } + else + { + OVRBarrelDistortionMonoPostFX.isEnabled = true; + } + + // Reset all sensors + ovrResetAllSensors(); +} + +// Call this function when ever you wish to turn off the stereo rendering +// and barrel distortion for the Rift. +function disableOculusVRDisplay(%gameConnection) +{ + %gameConnection.clearDisplayDevice(); + PlayGui.renderStyle = "standard"; + OVRBarrelDistortionPostFX.isEnabled = false; + OVRBarrelDistortionMonoPostFX.isEnabled = false; +} + +// Helper function to set the standard Rift control scheme. You could place +// this function in GameConnection::initialControlSet() at the same time +// you call enableOculusVRDisplay(). +function setStandardOculusVRControlScheme(%gameConnection) +{ + if(isOVRHMDSimulated(0)) + { + // We are simulating a HMD so allow the mouse and gamepad to control + // both yaw and pitch. + %gameConnection.setControlSchemeParameters(true, true, true); + } + else + { + // A HMD is connected so have the mouse and gamepad only add to yaw + %gameConnection.setControlSchemeParameters(true, true, false); + } +} + +//----------------------------------------------------------------------------- + +// Helper function to set the resolution for the Rift. +// Parameters: +// %fullscreen - If true then the display will be forced to full screen. If +// pointCanvasToOculusVRDisplay() was called before the Canvas was created, then +// the full screen display will appear on the Rift. +function setVideoModeForOculusVRDisplay(%fullscreen) +{ + %res = getOVRHMDResolution(0); + Canvas.setVideoMode(%res.x, %res.y, %fullscreen, 32, 0); +} + +//----------------------------------------------------------------------------- + +// Reset all Oculus Rift sensors. This will make the Rift's current heading +// be considered the origin. +function resetOculusVRSensors() +{ + ovrResetAllSensors(); +} diff --git a/Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs b/Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs new file mode 100644 index 0000000000..7978a6bc8a --- /dev/null +++ b/Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs @@ -0,0 +1,123 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Only load these shaders if an Oculus VR device is present +if(!isFunction(isOculusVRDeviceActive)) + return; + +//----------------------------------------------------------------------------- +// Shader data +//----------------------------------------------------------------------------- + +singleton ShaderData( OVRMonoToStereoShader ) +{ + DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/oculusvr/monoToStereoP.hlsl"; + + pixVersion = 2.0; +}; + +singleton ShaderData( OVRBarrelDistortionShader ) +{ + DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/oculusvr/barrelDistortionP.hlsl"; + + pixVersion = 2.0; +}; + +//----------------------------------------------------------------------------- +// GFX state blocks +//----------------------------------------------------------------------------- + +singleton GFXStateBlockData( OVRBarrelDistortionStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +//----------------------------------------------------------------------------- +// Barrel Distortion PostFx +// +// To be used with the Oculus Rift. +// Expects a stereo pair to exist on the back buffer and then applies the +// appropriate barrel distortion. +//----------------------------------------------------------------------------- +singleton BarrelDistortionPostEffect( OVRBarrelDistortionPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + renderPriority = 100; + + // The barrel distortion + shader = OVRBarrelDistortionShader; + stateBlock = OVRBarrelDistortionStateBlock; + + texture[0] = "$backBuffer"; + + scaleOutput = 1.25; +}; + +//----------------------------------------------------------------------------- +// Barrel Distortion Mono PostFx +// +// To be used with the Oculus Rift. +// Takes a non-stereo image and turns it into a stereo pair with barrel +// distortion applied. Only a vertical slice around the center of the back +// buffer is used to generate the pseudo stereo pair. +//----------------------------------------------------------------------------- +singleton PostEffect( OVRBarrelDistortionMonoPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + renderPriority = 100; + + // Converts the mono display to a stereo one + shader = OVRMonoToStereoShader; + stateBlock = OVRBarrelDistortionStateBlock; + + texture[0] = "$backBuffer"; + target = "$outTex"; + + // The actual barrel distortion + new BarrelDistortionPostEffect(OVRBarrelDistortionMonoStage2PostFX) + { + shader = OVRBarrelDistortionShader; + stateBlock = OVRBarrelDistortionStateBlock; + texture[0] = "$inTex"; + target = "$backBuffer"; + + scaleOutput = 1.25; + }; + +}; + +function OVRBarrelDistortionMonoPostFX::setShaderConsts( %this ) +{ + %HMDIndex = 0; + + %xOffsets = getOVRHMDEyeXOffsets(%HMDIndex); + %this.setShaderConst( "$LensXOffsets", %xOffsets ); +} diff --git a/Templates/Empty/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl b/Templates/Empty/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl new file mode 100644 index 0000000000..a09b1e9b22 --- /dev/null +++ b/Templates/Empty/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "shadergen:/autogenConditioners.h" +#include "../postFx.hlsl" +#include "../../torque.hlsl" + +uniform sampler2D backBuffer : register(S0); + +uniform float3 LensCenter; // x=Left X, y=Right X, z=Y +uniform float2 ScreenCenter; +uniform float2 Scale; +uniform float2 ScaleIn; +uniform float4 HmdWarpParam; + +// Scales input texture coordinates for distortion. +// ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be +// larger due to aspect ratio. +float2 HmdWarp(float2 in01, float2 lensCenter) +{ + float2 theta = (in01 - lensCenter) * ScaleIn; // Scales to [-1, 1] + float rSq = theta.x * theta.x + theta.y * theta.y; + float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq); + return lensCenter + Scale * theta1; +} + +float4 main( PFXVertToPix IN ) : COLOR0 +{ + float2 texCoord; + float xOffset; + float2 lensCenter; + lensCenter.y = LensCenter.z; + if(IN.uv0.x < 0.5) + { + texCoord.x = IN.uv0.x; + texCoord.y = IN.uv0.y; + xOffset = 0.0; + lensCenter.x = LensCenter.x; + } + else + { + texCoord.x = IN.uv0.x - 0.5; + texCoord.y = IN.uv0.y; + xOffset = 0.5; + lensCenter.x = LensCenter.y; + } + + float2 tc = HmdWarp(texCoord, lensCenter); + + float4 color; + if (any(clamp(tc, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tc)) + { + color = float4(0,0,0,0); + } + else + { + tc.x += xOffset; + color = tex2D(backBuffer, tc); + } + + return color; +} diff --git a/Templates/Empty/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl b/Templates/Empty/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl new file mode 100644 index 0000000000..ef5077d97d --- /dev/null +++ b/Templates/Empty/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "shadergen:/autogenConditioners.h" +#include "../postFx.hlsl" +#include "../../torque.hlsl" + +uniform sampler2D backBuffer : register(S0); + +uniform float2 LensXOffsets; + +float4 main( PFXVertToPix IN ) : COLOR0 +{ + float2 texCoord; + float xOffset; + float2 lensCenter; + lensCenter.y = 0.5; + if(IN.uv0.x < 0.5) + { + texCoord.x = IN.uv0.x; + texCoord.y = IN.uv0.y; + xOffset = 0.0; + lensCenter.x = LensXOffsets.x; + } + else + { + texCoord.x = IN.uv0.x - 0.5; + texCoord.y = IN.uv0.y; + xOffset = 0.5; + lensCenter.x = LensXOffsets.y; + } + + texCoord.x *= 2.0; + texCoord.x += lensCenter.x; + texCoord.x *= 0.5; + texCoord.x += 0.25; + + float4 color = tex2D(backBuffer, texCoord); + + return color; +} diff --git a/Templates/Full/game/core/scripts/client/core.cs b/Templates/Full/game/core/scripts/client/core.cs index 2213d982a1..da26beeffd 100644 --- a/Templates/Full/game/core/scripts/client/core.cs +++ b/Templates/Full/game/core/scripts/client/core.cs @@ -54,6 +54,9 @@ function initializeCore() exec( "./audioStates.cs" ); exec( "./audioAmbiences.cs" ); + // Input devices + exec("~/scripts/client/oculusVR.cs"); + // Seed the random number generator. setRandomSeed(); diff --git a/Templates/Full/game/core/scripts/client/oculusVR.cs b/Templates/Full/game/core/scripts/client/oculusVR.cs new file mode 100644 index 0000000000..81015b40f3 --- /dev/null +++ b/Templates/Full/game/core/scripts/client/oculusVR.cs @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Only load these functions if an Oculus VR device is present +if(!isFunction(isOculusVRDeviceActive)) + return; + +//----------------------------------------------------------------------------- + +function oculusSensorMetricsCallback() +{ + return " | OVR Sensor 0 |" @ + " rot: " @ getOVRSensorEulerRotation(0); +} + +//----------------------------------------------------------------------------- + +// Call this function from createCanvas() to have the Canvas attach itself +// to the Rift's display. The Canvas' window will still open on the primary +// display if that is different from the Rift, but it will move to the Rift +// when it goes full screen. If the Rift is not connected then nothing +// will happen. +function pointCanvasToOculusVRDisplay() +{ + $pref::Video::displayOutputDevice = getOVRHMDDisplayDeviceName(0); +} + +//----------------------------------------------------------------------------- + +// Call this function from GameConnection::initialControlSet() just before +// your "Canvas.setContent(PlayGui);" call, or at any time you wish to switch +// to a side-by-side rendering and the appropriate barrel distortion. This +// will turn on side-by-side rendering and tell the GameConnection to use the +// Rift as its display device. +// Parameters: +// %gameConnection - The client GameConnection instance +// %trueStereoRendering - If true will enable stereo rendering with an eye +// offset for each viewport. This will render each frame twice. If false +// then a pseudo stereo rendering is done with only a single render per frame. +function enableOculusVRDisplay(%gameConnection, %trueStereoRendering) +{ + setOVRHMDAsGameConnectionDisplayDevice(%gameConnection); + PlayGui.renderStyle = "stereo side by side"; + + if(%trueStereoRendering) + { + OVRBarrelDistortionPostFX.isEnabled = true; + } + else + { + OVRBarrelDistortionMonoPostFX.isEnabled = true; + } + + // Reset all sensors + ovrResetAllSensors(); +} + +// Call this function when ever you wish to turn off the stereo rendering +// and barrel distortion for the Rift. +function disableOculusVRDisplay(%gameConnection) +{ + %gameConnection.clearDisplayDevice(); + PlayGui.renderStyle = "standard"; + OVRBarrelDistortionPostFX.isEnabled = false; + OVRBarrelDistortionMonoPostFX.isEnabled = false; +} + +// Helper function to set the standard Rift control scheme. You could place +// this function in GameConnection::initialControlSet() at the same time +// you call enableOculusVRDisplay(). +function setStandardOculusVRControlScheme(%gameConnection) +{ + if(isOVRHMDSimulated(0)) + { + // We are simulating a HMD so allow the mouse and gamepad to control + // both yaw and pitch. + %gameConnection.setControlSchemeParameters(true, true, true); + } + else + { + // A HMD is connected so have the mouse and gamepad only add to yaw + %gameConnection.setControlSchemeParameters(true, true, false); + } +} + +//----------------------------------------------------------------------------- + +// Helper function to set the resolution for the Rift. +// Parameters: +// %fullscreen - If true then the display will be forced to full screen. If +// pointCanvasToOculusVRDisplay() was called before the Canvas was created, then +// the full screen display will appear on the Rift. +function setVideoModeForOculusVRDisplay(%fullscreen) +{ + %res = getOVRHMDResolution(0); + Canvas.setVideoMode(%res.x, %res.y, %fullscreen, 32, 0); +} + +//----------------------------------------------------------------------------- + +// Reset all Oculus Rift sensors. This will make the Rift's current heading +// be considered the origin. +function resetOculusVRSensors() +{ + ovrResetAllSensors(); +} diff --git a/Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs b/Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs new file mode 100644 index 0000000000..7978a6bc8a --- /dev/null +++ b/Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs @@ -0,0 +1,123 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Only load these shaders if an Oculus VR device is present +if(!isFunction(isOculusVRDeviceActive)) + return; + +//----------------------------------------------------------------------------- +// Shader data +//----------------------------------------------------------------------------- + +singleton ShaderData( OVRMonoToStereoShader ) +{ + DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/oculusvr/monoToStereoP.hlsl"; + + pixVersion = 2.0; +}; + +singleton ShaderData( OVRBarrelDistortionShader ) +{ + DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/oculusvr/barrelDistortionP.hlsl"; + + pixVersion = 2.0; +}; + +//----------------------------------------------------------------------------- +// GFX state blocks +//----------------------------------------------------------------------------- + +singleton GFXStateBlockData( OVRBarrelDistortionStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +//----------------------------------------------------------------------------- +// Barrel Distortion PostFx +// +// To be used with the Oculus Rift. +// Expects a stereo pair to exist on the back buffer and then applies the +// appropriate barrel distortion. +//----------------------------------------------------------------------------- +singleton BarrelDistortionPostEffect( OVRBarrelDistortionPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + renderPriority = 100; + + // The barrel distortion + shader = OVRBarrelDistortionShader; + stateBlock = OVRBarrelDistortionStateBlock; + + texture[0] = "$backBuffer"; + + scaleOutput = 1.25; +}; + +//----------------------------------------------------------------------------- +// Barrel Distortion Mono PostFx +// +// To be used with the Oculus Rift. +// Takes a non-stereo image and turns it into a stereo pair with barrel +// distortion applied. Only a vertical slice around the center of the back +// buffer is used to generate the pseudo stereo pair. +//----------------------------------------------------------------------------- +singleton PostEffect( OVRBarrelDistortionMonoPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + renderPriority = 100; + + // Converts the mono display to a stereo one + shader = OVRMonoToStereoShader; + stateBlock = OVRBarrelDistortionStateBlock; + + texture[0] = "$backBuffer"; + target = "$outTex"; + + // The actual barrel distortion + new BarrelDistortionPostEffect(OVRBarrelDistortionMonoStage2PostFX) + { + shader = OVRBarrelDistortionShader; + stateBlock = OVRBarrelDistortionStateBlock; + texture[0] = "$inTex"; + target = "$backBuffer"; + + scaleOutput = 1.25; + }; + +}; + +function OVRBarrelDistortionMonoPostFX::setShaderConsts( %this ) +{ + %HMDIndex = 0; + + %xOffsets = getOVRHMDEyeXOffsets(%HMDIndex); + %this.setShaderConst( "$LensXOffsets", %xOffsets ); +} diff --git a/Templates/Full/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl b/Templates/Full/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl new file mode 100644 index 0000000000..a09b1e9b22 --- /dev/null +++ b/Templates/Full/game/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "shadergen:/autogenConditioners.h" +#include "../postFx.hlsl" +#include "../../torque.hlsl" + +uniform sampler2D backBuffer : register(S0); + +uniform float3 LensCenter; // x=Left X, y=Right X, z=Y +uniform float2 ScreenCenter; +uniform float2 Scale; +uniform float2 ScaleIn; +uniform float4 HmdWarpParam; + +// Scales input texture coordinates for distortion. +// ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be +// larger due to aspect ratio. +float2 HmdWarp(float2 in01, float2 lensCenter) +{ + float2 theta = (in01 - lensCenter) * ScaleIn; // Scales to [-1, 1] + float rSq = theta.x * theta.x + theta.y * theta.y; + float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq); + return lensCenter + Scale * theta1; +} + +float4 main( PFXVertToPix IN ) : COLOR0 +{ + float2 texCoord; + float xOffset; + float2 lensCenter; + lensCenter.y = LensCenter.z; + if(IN.uv0.x < 0.5) + { + texCoord.x = IN.uv0.x; + texCoord.y = IN.uv0.y; + xOffset = 0.0; + lensCenter.x = LensCenter.x; + } + else + { + texCoord.x = IN.uv0.x - 0.5; + texCoord.y = IN.uv0.y; + xOffset = 0.5; + lensCenter.x = LensCenter.y; + } + + float2 tc = HmdWarp(texCoord, lensCenter); + + float4 color; + if (any(clamp(tc, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tc)) + { + color = float4(0,0,0,0); + } + else + { + tc.x += xOffset; + color = tex2D(backBuffer, tc); + } + + return color; +} diff --git a/Templates/Full/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl b/Templates/Full/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl new file mode 100644 index 0000000000..ef5077d97d --- /dev/null +++ b/Templates/Full/game/shaders/common/postFx/oculusvr/monoToStereoP.hlsl @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "shadergen:/autogenConditioners.h" +#include "../postFx.hlsl" +#include "../../torque.hlsl" + +uniform sampler2D backBuffer : register(S0); + +uniform float2 LensXOffsets; + +float4 main( PFXVertToPix IN ) : COLOR0 +{ + float2 texCoord; + float xOffset; + float2 lensCenter; + lensCenter.y = 0.5; + if(IN.uv0.x < 0.5) + { + texCoord.x = IN.uv0.x; + texCoord.y = IN.uv0.y; + xOffset = 0.0; + lensCenter.x = LensXOffsets.x; + } + else + { + texCoord.x = IN.uv0.x - 0.5; + texCoord.y = IN.uv0.y; + xOffset = 0.5; + lensCenter.x = LensXOffsets.y; + } + + texCoord.x *= 2.0; + texCoord.x += lensCenter.x; + texCoord.x *= 0.5; + texCoord.x += 0.25; + + float4 color = tex2D(backBuffer, texCoord); + + return color; +} diff --git a/Tools/projectGenerator/modules/oculusVR.inc b/Tools/projectGenerator/modules/oculusVR.inc new file mode 100644 index 0000000000..4945a9ad9b --- /dev/null +++ b/Tools/projectGenerator/modules/oculusVR.inc @@ -0,0 +1,80 @@ +