Update Oculus integration

This commit is contained in:
Bradley Austin Davis 2018-06-06 11:57:36 -07:00
parent aa19794a94
commit e38059797d
9 changed files with 359 additions and 357 deletions

View file

@ -17,8 +17,8 @@ if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://static.oculus.com/sdk-downloads/1.11.0/Public/1486063832/ovr_sdk_win_1.11.0_public.zip
URL_MD5 ea484403757cbfdfa743b6577fb1f9d2
URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.26.0_public.zip
URL_MD5 06804ff9727b910dcd04a37c800053b5
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
LOG_DOWNLOAD 1

View file

@ -4,7 +4,7 @@ project(LibOVR)
include_directories(LibOVR/Include LibOVR/Src)
file(GLOB HEADER_FILES LibOVR/Include/*.h)
file(GLOB EXTRA_HEADER_FILES LibOVR/Include/Extras/*.h)
file(GLOB_RECURSE SOURCE_FILES LibOVR/Src/*.c LibOVR/Src/*.cpp)
file(GLOB_RECURSE SOURCE_FILES LibOVR/Shim/*.c LibOVR/Shim/*.cpp)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DOVR_BUILD_DEBUG")
add_library(LibOVR STATIC ${SOURCE_FILES} ${HEADER_FILES} ${EXTRA_HEADER_FILES})
set_target_properties(LibOVR PROPERTIES DEBUG_POSTFIX "d")

View file

@ -15,19 +15,25 @@
#include "OculusHelpers.h"
using namespace hifi;
void OculusBaseDisplayPlugin::resetSensors() {
ovr_RecenterTrackingOrigin(_session);
_currentRenderFrameInfo.renderPose = glm::mat4(); // identity
}
bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
handleOVREvents();
if (quitRequested()) {
ovrSessionStatus status{};
if (!OVR_SUCCESS(ovr_GetSessionStatus(_session, &status))) {
qCWarning(oculusLog) << "Unable to fetch Oculus session status" << ovr::getError();
return false;
}
if (ovr::quitRequested(status)) {
QMetaObject::invokeMethod(qApp, "quit");
return false;
}
if (reorientRequested()) {
if (ovr::reorientRequested(status)) {
emit resetSensorsRequested();
}
@ -35,18 +41,18 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
_currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrTrue);
_currentRenderFrameInfo.renderPose = toGlm(trackingState.HeadPose.ThePose);
_currentRenderFrameInfo.renderPose = ovr::toGlm(trackingState.HeadPose.ThePose);
_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
std::array<glm::mat4, 2> handPoses;
// Make controller poses available to the presentation thread
ovr_for_each_hand([&](ovrHandType hand) {
ovr::for_each_hand([&](ovrHandType hand) {
static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked;
if (REQUIRED_HAND_STATUS != (trackingState.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) {
return;
}
auto correctedPose = ovrControllerPoseToHandPose(hand, trackingState.HandPoses[hand]);
auto correctedPose = ovr::toControllerPose(hand, trackingState.HandPoses[hand]);
static const glm::quat HAND_TO_LASER_ROTATION = glm::rotation(Vectors::UNIT_Z, Vectors::UNIT_NEG_Y);
handPoses[hand] = glm::translate(glm::mat4(), correctedPose.translation) * glm::mat4_cast(correctedPose.rotation * HAND_TO_LASER_ROTATION);
});
@ -58,7 +64,7 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
}
bool OculusBaseDisplayPlugin::isSupported() const {
return oculusAvailable();
return ovr::available();
}
glm::mat4 OculusBaseDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const {
@ -71,7 +77,7 @@ glm::mat4 OculusBaseDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& ba
ovrFovPort fovPort = _hmdDesc.DefaultEyeFov[eye];
ovrEyeRenderDesc& erd = ovr_GetRenderDesc(_session, ovrEye, fovPort);
ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(erd.Fov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL);
return toGlm(ovrPerspectiveProjection);
return ovr::toGlm(ovrPerspectiveProjection);
} else {
return baseProjection;
}
@ -85,7 +91,7 @@ glm::mat4 OculusBaseDisplayPlugin::getCullingProjection(const glm::mat4& basePro
float baseFarClip = baseFrustum.getFarClip();
auto combinedFov = _eyeFovs[0];
combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan);
return toGlm(ovrMatrix4f_Projection(combinedFov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL));
return ovr::toGlm(ovrMatrix4f_Projection(combinedFov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL));
} else {
return baseProjection;
}
@ -102,7 +108,7 @@ void OculusBaseDisplayPlugin::uncustomizeContext() {
}
bool OculusBaseDisplayPlugin::internalActivate() {
_session = acquireOculusSession();
_session = ovr::acquireRenderSession();
if (!_session) {
return false;
}
@ -113,21 +119,21 @@ bool OculusBaseDisplayPlugin::internalActivate() {
_viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
_ipd = 0;
ovr_for_each_eye([&](ovrEyeType eye) {
ovr::for_each_eye([&](ovrEyeType eye) {
_eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_session, eye, _eyeFovs[eye]);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL);
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = glm::translate(mat4(), toGlm(erd.HmdToEyeOffset));
eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f));
_viewScaleDesc.HmdToEyeOffset[eye] = erd.HmdToEyeOffset;
_ipd += glm::abs(glm::length(toGlm(erd.HmdToEyeOffset)));
_eyeProjections[eye] = ovr::toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = ovr::toGlm(erd.HmdToEyePose);
eyeSizes[eye] = ovr::toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f));
_viewScaleDesc.HmdToEyePose[eye] = erd.HmdToEyePose;
_ipd += glm::abs(glm::length(ovr::toGlm(erd.HmdToEyePose.Position)));
});
auto combinedFov = _eyeFovs[0];
combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan);
_cullingProjection = toGlm(ovrMatrix4f_Projection(combinedFov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL));
_cullingProjection = ovr::toGlm(ovrMatrix4f_Projection(combinedFov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL));
_renderTargetSize = uvec2(
eyeSizes[0].x + eyeSizes[1].x,
@ -136,7 +142,7 @@ bool OculusBaseDisplayPlugin::internalActivate() {
memset(&_sceneLayer, 0, sizeof(ovrLayerEyeFov));
_sceneLayer.Header.Type = ovrLayerType_EyeFov;
_sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
ovr_for_each_eye([&](ovrEyeType eye) {
ovr::for_each_eye([&](ovrEyeType eye) {
ovrFovPort & fov = _sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov;
ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_session, eye, fov, 1.0f);
_sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
@ -150,28 +156,14 @@ bool OculusBaseDisplayPlugin::internalActivate() {
void OculusBaseDisplayPlugin::internalDeactivate() {
Parent::internalDeactivate();
ovr::releaseRenderSession(_session);
}
bool OculusBaseDisplayPlugin::activateStandBySession() {
if (!_session) {
_session = acquireOculusSession();
}
return _session;
}
void OculusBaseDisplayPlugin::deactivateSession() {
// FIXME
// Switching to Qt 5.9 exposed a race condition or similar issue that caused a crash when putting on an Rift
// while already in VR mode. Commenting these out is a workaround.
//releaseOculusSession();
//_session = nullptr;
}
void OculusBaseDisplayPlugin::updatePresentPose() {
ovrTrackingState trackingState;
_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0);
auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose);
trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
_currentPresentFrameInfo.presentPose = ovr::toGlm(trackingState.HeadPose.ThePose);
_currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose;
}
OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() {
}

View file

@ -16,37 +16,32 @@
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin;
public:
~OculusBaseDisplayPlugin();
bool isSupported() const override;
bool hasAsyncReprojection() const override { return true; }
bool getSupportsAutoSwitch() override final { return true; }
glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override;
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override;
bool hasAsyncReprojection() const override { return true; }
// Stereo specific methods
void resetSensors() override final;
bool beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
bool getSupportsAutoSwitch() override final { return true; }
protected:
void customizeContext() override;
void uncustomizeContext() override;
bool internalActivate() override;
void internalDeactivate() override;
bool activateStandBySession() override;
void deactivateSession() override;
void updatePresentPose() override;
protected:
ovrSession _session { nullptr };
ovrSession _session{ nullptr };
ovrGraphicsLuid _luid;
ovrEyeRenderDesc _eyeRenderDescs[2];
ovrFovPort _eyeFovs[2];
std::array<ovrEyeRenderDesc, 2> _eyeRenderDescs;
std::array<ovrFovPort, 2> _eyeFovs;
ovrHmdDesc _hmdDesc;
ovrLayerEyeFov _sceneLayer;
ovrViewScaleDesc _viewScaleDesc;
// ovrLayerEyeFovDepth _depthLayer;
};

View file

@ -22,34 +22,23 @@
#include <NumericalConstants.h>
#include <StreamUtils.h>
#include <OVR_CAPI.h>
#include <ovr_capi.h>
#include "OculusHelpers.h"
Q_DECLARE_LOGGING_CATEGORY(oculus)
static const char* MENU_PARENT = "Avatar";
static const char* MENU_NAME = "Oculus Touch Controllers";
static const char* MENU_PATH = "Avatar" ">" "Oculus Touch Controllers";
using namespace hifi;
const char* OculusControllerManager::NAME = "Oculus";
const quint64 LOST_TRACKING_DELAY = 3000000;
bool OculusControllerManager::isSupported() const {
return oculusAvailable();
return hifi::ovr::available();
}
bool OculusControllerManager::activate() {
InputPlugin::activate();
if (!_session) {
_session = acquireOculusSession();
}
Q_ASSERT(_session);
checkForConnectedDevices();
return true;
}
@ -58,33 +47,30 @@ void OculusControllerManager::checkForConnectedDevices() {
return;
}
unsigned int controllerConnected = ovr_GetConnectedControllerTypes(_session);
ovr::withSession([&] (ovrSession session) {
unsigned int controllerConnected = ovr_GetConnectedControllerTypes(session);
if (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) {
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
_remote = std::make_shared<RemoteDevice>(*this);
userInputMapper->registerDevice(_remote);
if (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) {
if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_inputState))) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
_remote = std::make_shared<RemoteDevice>(*this);
userInputMapper->registerDevice(_remote);
}
}
}
if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) {
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
_touch = std::make_shared<TouchDevice>(*this);
userInputMapper->registerDevice(_touch);
if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) {
if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_inputState))) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
_touch = std::make_shared<TouchDevice>(*this);
userInputMapper->registerDevice(_touch);
}
}
}
});
}
void OculusControllerManager::deactivate() {
InputPlugin::deactivate();
if (_session) {
releaseOculusSession();
_session = nullptr;
}
// unregister with UserInputMapper
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
if (_touch) {
@ -100,20 +86,30 @@ void OculusControllerManager::pluginUpdate(float deltaTime, const controller::In
checkForConnectedDevices();
if (_touch) {
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) {
_touch->update(deltaTime, inputCalibrationData);
} else {
qCWarning(oculus) << "Unable to read Oculus touch input state";
bool updateRemote = false, updateTouch = false;
ovr::withSession([&](ovrSession session) {
if (_touch) {
updateTouch = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_inputState));
if (!updateTouch) {
qCWarning(oculusLog) << "Unable to read Oculus touch input state" << ovr::getError();
}
}
if (_remote) {
updateRemote = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_inputState));
if (!updateRemote) {
qCWarning(oculusLog) << "Unable to read Oculus remote input state" << ovr::getError();
}
}
});
if (_touch && updateTouch) {
_touch->update(deltaTime, inputCalibrationData);
}
if (_remote) {
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) {
_remote->update(deltaTime, inputCalibrationData);
} else {
qCWarning(oculus) << "Unable to read Oculus remote input state";
}
if (_remote && updateRemote) {
_remote->update(deltaTime, inputCalibrationData);
}
}
@ -210,15 +206,6 @@ void OculusControllerManager::RemoteDevice::focusOutEvent() {
_buttonPressedMap.clear();
}
bool OculusControllerManager::isHeadControllerMounted() const {
ovrSessionStatus status;
bool success = OVR_SUCCESS(ovr_GetSessionStatus(_session, &status));
if (!success) {
return false;
}
return status.HmdMounted == ovrTrue;
}
void OculusControllerManager::TouchDevice::update(float deltaTime,
const controller::InputCalibrationData& inputCalibrationData) {
_buttonPressedMap.clear();
@ -226,10 +213,19 @@ void OculusControllerManager::TouchDevice::update(float deltaTime,
int numTrackedControllers = 0;
quint64 currentTime = usecTimestampNow();
static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked;
auto tracking = ovr_GetTrackingState(_parent._session, 0, false);
ovr_for_each_hand([&](ovrHandType hand) {
bool hasInputFocus = ovr::hasInputFocus();
auto tracking = ovr::getTrackingState(); // ovr_GetTrackingState(_parent._session, 0, false);
ovr::for_each_hand([&](ovrHandType hand) {
++numTrackedControllers;
int controller = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND);
// Disable hand tracking while in Oculus Dash (Dash renders it's own hands)
if (!hasInputFocus) {
_poseStateMap.erase(controller);
_poseStateMap[controller].valid = false;
return;
}
if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) {
_poseStateMap.erase(controller);
handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]);
@ -253,7 +249,7 @@ void OculusControllerManager::TouchDevice::update(float deltaTime,
handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]);
});
if (_parent.isHeadControllerMounted()) {
if (ovr::hmdMounted()) {
handleHeadPose(deltaTime, inputCalibrationData, tracking.HeadPose);
} else {
_poseStateMap[controller::HEAD].valid = false;
@ -311,7 +307,7 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
ovrHandType hand, const ovrPoseStatef& handPose) {
auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND;
auto& pose = _poseStateMap[poseId];
pose = ovrControllerPoseToHandPose(hand, handPose);
pose = ovr::toControllerPose(hand, handPose);
// transform into avatar frame
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
pose = pose.transform(controllerToAvatar);
@ -320,15 +316,15 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime,
const controller::InputCalibrationData& inputCalibrationData,
const ovrPoseStatef& headPose) {
glm::mat4 mat = createMatFromQuatAndPos(toGlm(headPose.ThePose.Orientation),
toGlm(headPose.ThePose.Position));
glm::mat4 mat = createMatFromQuatAndPos(ovr::toGlm(headPose.ThePose.Orientation),
ovr::toGlm(headPose.ThePose.Position));
//perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z
glm::mat4 matYFlip = mat * Matrices::Y_180;
controller::Pose pose(extractTranslation(matYFlip),
glmExtractRotation(matYFlip),
toGlm(headPose.LinearVelocity), // XXX * matYFlip ?
toGlm(headPose.AngularVelocity));
ovr::toGlm(headPose.LinearVelocity), // XXX * matYFlip ?
ovr::toGlm(headPose.AngularVelocity));
glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) *
@ -343,7 +339,7 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const
auto poseId = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND);
auto& pose = _poseStateMap[poseId];
auto lastHandPose = _lastControllerPose[poseId];
pose = ovrControllerRotationToHandRotation(hand, handPose, lastHandPose);
pose = ovr::toControllerPose(hand, handPose, lastHandPose);
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
pose = pose.transform(controllerToAvatar);
}
@ -351,36 +347,40 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const
bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock);
bool toReturn = true;
if (hand == controller::BOTH || hand == controller::LEFT) {
if (strength == 0.0f) {
_leftHapticStrength = 0.0f;
_leftHapticDuration = 0.0f;
} else {
_leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength;
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) {
toReturn = false;
ovr::withSession([&](ovrSession session) {
if (hand == controller::BOTH || hand == controller::LEFT) {
if (strength == 0.0f) {
_leftHapticStrength = 0.0f;
_leftHapticDuration = 0.0f;
} else {
_leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength;
if (ovr_SetControllerVibration(session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) {
toReturn = false;
}
_leftHapticDuration = std::max(duration, _leftHapticDuration);
}
_leftHapticDuration = std::max(duration, _leftHapticDuration);
}
}
if (hand == controller::BOTH || hand == controller::RIGHT) {
if (strength == 0.0f) {
_rightHapticStrength = 0.0f;
_rightHapticDuration = 0.0f;
} else {
_rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength;
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) {
toReturn = false;
if (hand == controller::BOTH || hand == controller::RIGHT) {
if (strength == 0.0f) {
_rightHapticStrength = 0.0f;
_rightHapticDuration = 0.0f;
} else {
_rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength;
if (ovr_SetControllerVibration(session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) {
toReturn = false;
}
_rightHapticDuration = std::max(duration, _rightHapticDuration);
}
_rightHapticDuration = std::max(duration, _rightHapticDuration);
}
}
});
return toReturn;
}
void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch);
ovr_SetControllerVibration(_parent._session, handType, 0.0f, 0.0f);
ovr::withSession([&](ovrSession session) {
ovr_SetControllerVibration(session, handType, 0.0f, 0.0f);
});
}
/**jsdoc

View file

@ -26,10 +26,8 @@ public:
// Plugin functions
bool isSupported() const override;
const QString getName() const override { return NAME; }
bool isHandController() const override { return _touch != nullptr; }
bool isHeadController() const override { return true; }
bool isHeadControllerMounted() const;
QStringList getSubdeviceNames() override;
bool activate() override;
@ -105,7 +103,6 @@ private:
void checkForConnectedDevices();
ovrSession _session { nullptr };
ovrInputState _inputState {};
RemoteDevice::Pointer _remote;
TouchDevice::Pointer _touch;

View file

@ -19,6 +19,8 @@
#include "OculusHelpers.h"
using namespace hifi;
const char* OculusDisplayPlugin::NAME { "Oculus Rift" };
static ovrPerfHudMode currentDebugMode = ovrPerfHud_Off;
@ -63,7 +65,7 @@ void OculusDisplayPlugin::cycleDebugOutput() {
void OculusDisplayPlugin::customizeContext() {
Parent::customizeContext();
_outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OculusOutput", gpu::Element::COLOR_SRGBA_32, _renderTargetSize.x, _renderTargetSize.y));
_outputFramebuffer.reset(gpu::Framebuffer::create("OculusOutput", gpu::Element::COLOR_SRGBA_32, _renderTargetSize.x, _renderTargetSize.y));
ovrTextureSwapChainDesc desc = { };
desc.Type = ovrTexture_2D;
desc.ArraySize = 1;
@ -76,14 +78,14 @@ void OculusDisplayPlugin::customizeContext() {
ovrResult result = ovr_CreateTextureSwapChainGL(_session, &desc, &_textureSwapChain);
if (!OVR_SUCCESS(result)) {
logCritical("Failed to create swap textures");
qCritical(oculusLog) << "Failed to create swap textures" << ovr::getError();
return;
}
int length = 0;
result = ovr_GetTextureSwapChainLength(_session, _textureSwapChain, &length);
if (!OVR_SUCCESS(result) || !length) {
logCritical("Unable to count swap chain textures");
qCritical(oculusLog) << "Unable to count swap chain textures" << ovr::getError();
return;
}
for (int i = 0; i < length; ++i) {
@ -164,8 +166,8 @@ void OculusDisplayPlugin::hmdPresent() {
auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain);
Q_ASSERT(OVR_SUCCESS(result));
_sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime;
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose);
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose);
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovr::poseFromGlm(_currentPresentFrameInfo.renderPose);
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovr::poseFromGlm(_currentPresentFrameInfo.renderPose);
auto submitStart = usecTimestampNow();
uint64_t nonSubmitInterval = 0;
@ -192,7 +194,7 @@ void OculusDisplayPlugin::hmdPresent() {
}
if (!OVR_SUCCESS(result)) {
logWarning("Failed to present");
qWarning(oculusLog) << "Failed to present" << ovr::getError();
}
static int compositorDroppedFrames = 0;
@ -234,9 +236,7 @@ QJsonObject OculusDisplayPlugin::getHardwareStats() const {
}
bool OculusDisplayPlugin::isHmdMounted() const {
ovrSessionStatus status;
return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) &&
(ovrFalse != status.HmdMounted));
return ovr::hmdMounted();
}
QString OculusDisplayPlugin::getPreferredAudioInDevice() const {

View file

@ -24,41 +24,17 @@
#include <NumericalConstants.h>
Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display")
Q_LOGGING_CATEGORY(oculus, "hifi.plugins.display.oculus")
static std::atomic<uint32_t> refCount { 0 };
static ovrSession session { nullptr };
static bool _quitRequested { false };
static bool _reorientRequested { false };
inline ovrErrorInfo getError() {
ovrErrorInfo error;
ovr_GetLastErrorInfo(&error);
return error;
}
void logWarning(const char* what) {
qWarning(oculus) << what << ":" << getError().ErrorString;
}
void logCritical(const char* what) {
std::string error("[oculus] ");
error += what;
error += ": ";
error += getError().ErrorString;
qCritical(error.c_str());
}
Q_LOGGING_CATEGORY(oculusLog, "hifi.plugins.display.oculus")
using namespace hifi;
static wchar_t* REQUIRED_OCULUS_DLL = L"LibOVRRT64_1.dll";
static wchar_t FOUND_PATH[MAX_PATH];
bool oculusAvailable() {
bool ovr::available() {
static std::once_flag once;
static bool result { false };
static bool result{ false };
std::call_once(once, [&] {
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR");
static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
if (enableDebugOpenVR) {
@ -81,113 +57,107 @@ bool oculusAvailable() {
return result;
}
ovrSession acquireOculusSession() {
if (!session && !oculusAvailable()) {
qCDebug(oculus) << "oculus: no runtime or HMD present";
class ovrImpl {
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
std::mutex mutex;
ovrSession session{ nullptr };
size_t renderCount{ 0 };
private:
void setupSession(bool render) {
if (session) {
return;
}
ovrInitParams initParams{ ovrInit_RequestVersion | ovrInit_FocusAware, OVR_MINOR_VERSION, nullptr, 0, 0 };
if (render) {
initParams.Flags |= ovrInit_MixedRendering;
} else {
initParams.Flags |= ovrInit_Invisible;
}
if (!OVR_SUCCESS(ovr_Initialize(&initParams))) {
qCWarning(oculusLog) << "Failed to initialze Oculus SDK" << ovr::getError();
return;
}
ovrGraphicsLuid luid;
if (!OVR_SUCCESS(ovr_Create(&session, &luid))) {
qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError();
return;
}
}
void releaseSession() {
if (!session) {
return;
}
ovr_Destroy(session);
session = nullptr;
ovr_Shutdown();
}
public:
void withSession(const std::function<void(ovrSession)>& f) {
Lock lock(mutex);
if (!session) {
setupSession(false);
}
f(session);
}
ovrSession acquireRenderSession() {
Lock lock(mutex);
if (renderCount++ == 0) {
releaseSession();
setupSession(true);
}
return session;
}
if (!session) {
ovrInitParams initParams {
ovrInit_RequestVersion | ovrInit_MixedRendering, OVR_MINOR_VERSION, nullptr, 0, 0
};
if (!OVR_SUCCESS(ovr_Initialize(&initParams))) {
logWarning("Failed to initialize Oculus SDK");
return session;
}
#ifdef OCULUS_APP_ID
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) {
// we were unable to initialize the platform for entitlement check - fail the check
_quitRequested = true;
} else {
qCDebug(oculus) << "Performing Oculus Platform entitlement check";
ovr_Entitlement_GetIsViewerEntitled();
}
}
#endif
Q_ASSERT(0 == refCount);
ovrGraphicsLuid luid;
if (!OVR_SUCCESS(ovr_Create(&session, &luid))) {
logWarning("Failed to acquire Oculus session");
return session;
void releaseRenderSession(ovrSession session) {
Lock lock(mutex);
if (--renderCount == 0) {
releaseSession();
}
}
} _ovr;
++refCount;
return session;
ovrSession ovr::acquireRenderSession() {
return _ovr.acquireRenderSession();
}
void releaseOculusSession() {
Q_ASSERT(refCount > 0 && session);
// HACK the Oculus runtime doesn't seem to play well with repeated shutdown / restart.
// So for now we'll just hold on to the session
#if 0
if (!--refCount) {
qCDebug(oculus) << "oculus: zero refcount, shutdown SDK and session";
ovr_Destroy(session);
ovr_Shutdown();
session = nullptr;
}
#endif
void ovr::releaseRenderSession(ovrSession session) {
_ovr.releaseRenderSession(session);
}
void handleOVREvents() {
if (!session) {
return;
}
void ovr::withSession(const std::function<void(ovrSession)>& f) {
_ovr.withSession(f);
}
ovrSessionStatus status;
if (!OVR_SUCCESS(ovr_GetSessionStatus(session, &status))) {
return;
}
_quitRequested = status.ShouldQuit;
_reorientRequested = status.ShouldRecenter;
#ifdef OCULUS_APP_ID
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
// pop messages to see if we got a return for an entitlement check
ovrMessageHandle message = ovr_PopMessage();
while (message) {
switch (ovr_Message_GetType(message)) {
case ovrMessage_Entitlement_GetIsViewerEntitled: {
if (!ovr_Message_IsError(message)) {
// this viewer is entitled, no need to flag anything
qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally";
} else {
// we failed the entitlement check, set our flag so the app can stop
qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID;
_quitRequested = true;
}
}
}
// free the message handle to cleanup and not leak
ovr_FreeMessage(message);
// pop the next message to check, if there is one
message = ovr_PopMessage();
ovrSessionStatus ovr::getStatus() {
ovrSessionStatus status{};
withSession([&](ovrSession session) {
if (!OVR_SUCCESS(ovr_GetSessionStatus(session, &status))) {
qCWarning(oculusLog) << "Failed to get session status" << ovr::getError();
}
}
#endif
});
return status;
}
bool quitRequested() {
return _quitRequested;
}
bool reorientRequested() {
return _reorientRequested;
ovrTrackingState ovr::getTrackingState() {
ovrTrackingState result{};
withSession([&](ovrSession session) { result = ovr_GetTrackingState(session, 0, ovrFalse); });
return result;
}
controller::Pose ovrControllerPoseToHandPose(
ovrHandType hand,
const ovrPoseStatef& handPose) {
QString ovr::getError() {
static ovrErrorInfo error;
ovr_GetLastErrorInfo(&error);
return QString(error.ErrorString);
}
controller::Pose hifi::ovr::toControllerPose(ovrHandType hand, const ovrPoseStatef& handPose) {
// When the sensor-to-world rotation is identity the coordinate axes look like this:
//
// user
@ -247,9 +217,8 @@ controller::Pose ovrControllerPoseToHandPose(
static const glm::quat rightRotationOffset = glm::inverse(rightQuarterZ) * touchToHand;
static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f,
-CONTROLLER_LENGTH_OFFSET / 2.0f,
CONTROLLER_LENGTH_OFFSET * 1.5f);
static const glm::vec3 CONTROLLER_OFFSET =
glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, -CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET * 1.5f);
static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET;
static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET;
@ -268,12 +237,13 @@ controller::Pose ovrControllerPoseToHandPose(
return pose;
}
controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovrPoseStatef& handPose,
const ovrPoseStatef& lastHandPose) {
controller::Pose hifi::ovr::toControllerPose(ovrHandType hand,
const ovrPoseStatef& handPose,
const ovrPoseStatef& lastHandPose) {
static const glm::quat yFlip = glm::angleAxis(PI, Vectors::UNIT_Y);
static const glm::quat quarterX = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_X);
static const glm::quat touchToHand = yFlip * quarterX;
static const glm::quat leftQuarterZ = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_Z);
static const glm::quat rightQuarterZ = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_Z);
@ -281,9 +251,8 @@ controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovr
static const glm::quat rightRotationOffset = glm::inverse(rightQuarterZ) * touchToHand;
static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f,
-CONTROLLER_LENGTH_OFFSET / 2.0f,
CONTROLLER_LENGTH_OFFSET * 1.5f);
static const glm::vec3 CONTROLLER_OFFSET =
glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, -CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET * 1.5f);
static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET;
static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET;
@ -301,3 +270,52 @@ controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovr
pose.valid = true;
return pose;
}
// FIXME These should be moved to an oculusPlatform plugin, they don't interact with the controller or session state
#if 0
void handleOVREvents() {
updateSessionStatus(true);
#ifdef OCULUS_APP_ID
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
// pop messages to see if we got a return for an entitlement check
ovrMessageHandle message = ovr_PopMessage();
while (message) {
switch (ovr_Message_GetType(message)) {
case ovrMessage_Entitlement_GetIsViewerEntitled:
{
if (!ovr_Message_IsError(message)) {
// this viewer is entitled, no need to flag anything
qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally";
} else {
// we failed the entitlement check, set our flag so the app can stop
qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID;
_quitRequested = true;
}
}
}
// free the message handle to cleanup and not leak
ovr_FreeMessage(message);
// pop the next message to check, if there is one
message = ovr_PopMessage();
}
}
#endif
}
#ifdef OCULUS_APP_ID
if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) {
if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) {
// we were unable to initialize the platform for entitlement check - fail the check
_quitRequested = true;
} else {
qCDebug(oculusLog) << "Performing Oculus Platform entitlement check";
ovr_Entitlement_GetIsViewerEntitled();
}
}
#endif
#endif

View file

@ -7,10 +7,9 @@
//
#pragma once
#include <QtCore/QLoggingCategory>
#include <OVR_CAPI_GL.h>
#include <ovr_capi.h>
#include <GLMHelpers.h>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
@ -18,106 +17,107 @@
#include <controllers/Forward.h>
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
Q_DECLARE_LOGGING_CATEGORY(oculus)
Q_DECLARE_LOGGING_CATEGORY(oculusLog)
void logWarning(const char* what);
void logCritical(const char* what);
bool oculusAvailable();
ovrSession acquireOculusSession();
void releaseOculusSession();
namespace hifi {
struct ovr {
static bool available();
static ovrSession acquireRenderSession();
static void releaseRenderSession(ovrSession session);
static void withSession(const std::function<void(ovrSession)>& f);
static ovrSessionStatus getStatus();
static ovrTrackingState getTrackingState();
static QString getError();
void handleOVREvents();
bool quitRequested();
bool reorientRequested();
static inline bool quitRequested() { return quitRequested(getStatus()); }
static inline bool reorientRequested() { return reorientRequested(getStatus()); }
static inline bool hmdMounted() { return hmdMounted(getStatus()); }
static inline bool hasInputFocus() { return hasInputFocus(getStatus()); }
// Convenience method for looping over each eye with a lambda
template <typename Function>
inline void ovr_for_each_eye(Function function) {
for (ovrEyeType eye = ovrEyeType::ovrEye_Left;
eye < ovrEyeType::ovrEye_Count;
eye = static_cast<ovrEyeType>(eye + 1)) {
function(eye);
static inline bool quitRequested(const ovrSessionStatus& status) { return status.ShouldQuit != ovrFalse; }
static inline bool reorientRequested(const ovrSessionStatus& status) { return status.ShouldRecenter != ovrFalse; }
static inline bool hmdMounted(const ovrSessionStatus& status) { return status.HmdMounted != ovrFalse; }
static inline bool hasInputFocus(const ovrSessionStatus& status) { return status.HasInputFocus != ovrFalse; }
// Convenience method for looping over each eye with a lambda
static inline void for_each_eye(const std::function<void(ovrEyeType eye)>& f) {
for (ovrEyeType eye = ovrEye_Left; eye < ovrEye_Count; eye = static_cast<ovrEyeType>(eye + 1)) {
f(eye);
}
}
}
template <typename Function>
inline void ovr_for_each_hand(Function function) {
for (ovrHandType hand = ovrHandType::ovrHand_Left;
hand <= ovrHandType::ovrHand_Right;
hand = static_cast<ovrHandType>(hand + 1)) {
function(hand);
static inline void for_each_hand(const std::function<void(ovrHandType eye)>& f) {
for (ovrHandType hand = ovrHand_Left; hand < ovrHand_Count; hand = static_cast<ovrHandType>(hand + 1)) {
f(hand);
}
}
}
static inline glm::mat4 toGlm(const ovrMatrix4f& om) {
return glm::transpose(glm::make_mat4(&om.M[0][0]));
}
static inline glm::mat4 toGlm(const ovrFovPort& fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {
return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));
}
static inline glm::vec3 toGlm(const ovrVector3f& ov) {
return glm::make_vec3(&ov.x);
}
static inline glm::vec2 toGlm(const ovrVector2f& ov) {
return glm::make_vec2(&ov.x);
}
inline glm::mat4 toGlm(const ovrMatrix4f & om) {
return glm::transpose(glm::make_mat4(&om.M[0][0]));
}
static inline glm::uvec2 toGlm(const ovrSizei& ov) {
return glm::uvec2(ov.w, ov.h);
}
inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {
return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));
}
static inline glm::quat toGlm(const ovrQuatf& oq) {
return glm::make_quat(&oq.x);
}
inline glm::vec3 toGlm(const ovrVector3f & ov) {
return glm::make_vec3(&ov.x);
}
static inline glm::mat4 toGlm(const ovrPosef& op) {
glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));
glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));
return translation * orientation;
}
inline glm::vec2 toGlm(const ovrVector2f & ov) {
return glm::make_vec2(&ov.x);
}
static inline ovrMatrix4f fromGlm(const glm::mat4& m) {
ovrMatrix4f result;
glm::mat4 transposed(glm::transpose(m));
memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);
return result;
}
inline glm::uvec2 toGlm(const ovrSizei & ov) {
return glm::uvec2(ov.w, ov.h);
}
static inline ovrVector3f fromGlm(const glm::vec3& v) {
return { v.x, v.y, v.z };
}
inline glm::quat toGlm(const ovrQuatf & oq) {
return glm::make_quat(&oq.x);
}
static inline ovrVector2f fromGlm(const glm::vec2& v) {
return { v.x, v.y };
}
inline glm::mat4 toGlm(const ovrPosef & op) {
glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));
glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));
return translation * orientation;
}
static inline ovrSizei fromGlm(const glm::uvec2& v) {
return { (int)v.x, (int)v.y };
}
inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) {
ovrMatrix4f result;
glm::mat4 transposed(glm::transpose(m));
memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);
return result;
}
static inline ovrQuatf fromGlm(const glm::quat& q) {
return { q.x, q.y, q.z, q.w };
}
inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {
return{ v.x, v.y, v.z };
}
static inline ovrPosef poseFromGlm(const glm::mat4& m) {
glm::vec3 translation = glm::vec3(m[3]) / m[3].w;
glm::quat orientation = glm::quat_cast(m);
ovrPosef result;
result.Orientation = fromGlm(orientation);
result.Position = fromGlm(translation);
return result;
}
inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {
return{ v.x, v.y };
}
static controller::Pose toControllerPose(ovrHandType hand, const ovrPoseStatef& handPose);
static controller::Pose toControllerPose(ovrHandType hand, const ovrPoseStatef& handPose, const ovrPoseStatef& lastHandPose);
inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {
return{ (int)v.x, (int)v.y };
}
};
inline ovrQuatf ovrFromGlm(const glm::quat & q) {
return{ q.x, q.y, q.z, q.w };
}
inline ovrPosef ovrPoseFromGlm(const glm::mat4 & m) {
glm::vec3 translation = glm::vec3(m[3]) / m[3].w;
glm::quat orientation = glm::quat_cast(m);
ovrPosef result;
result.Orientation = ovrFromGlm(orientation);
result.Position = ovrFromGlm(translation);
return result;
}
controller::Pose ovrControllerPoseToHandPose(
ovrHandType hand,
const ovrPoseStatef& handPose);
controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand,
const ovrPoseStatef& handPose, const ovrPoseStatef& lastHandPose);
} // namespace hifi