This commit is contained in:
bjorn 2018-09-09 06:25:02 -07:00
parent 6b0806d04a
commit 5b5e9fbc00
2 changed files with 567 additions and 0 deletions

View File

@ -225,6 +225,13 @@ if(NOT EMSCRIPTEN)
)
endif()
# Oculus
if (NOT EMSCRIPTEN AND LOVR_OCULUS_PATH)
include_directories("${LOVR_OCULUS_PATH}/LibOVR/Include")
link_directories("${LOVR_OCULUS_PATH}/LibOVR/Lib/Windows/x64/Release/VS2017")
set(LOVR_OCULUS LibOVR)
endif()
# pthreads
if(NOT WIN32)
set(THREADS_PREFER_PTHREAD_FLAG ON)
@ -326,6 +333,10 @@ if(EMSCRIPTEN)
set(LOVR_SRC ${LOVR_SRC} src/headset/webvr.c)
else()
set(LOVR_SRC ${LOVR_SRC} src/headset/openvr.c)
if (LOVR_OCULUS_PATH)
set(LOVR_SRC ${LOVR_SRC} src/headset/oculus.c)
endif()
endif()
# LÖVR
@ -342,6 +353,7 @@ target_link_libraries(lovr
${LOVR_OPENAL}
${LOVR_OPENGL}
${LOVR_OPENVR}
${LOVR_OCULUS}
${LOVR_PHYSFS}
${LOVR_PTHREADS}
${LOVR_EMSCRIPTEN_FLAGS}

555
src/headset/oculus.c Normal file
View File

@ -0,0 +1,555 @@
#include "headset/headset.h"
#include "event/event.h"
#include "graphics/graphics.h"
#include "graphics/texture.h"
#include "lib/vec/vec.h"
#include "math/mat4.h"
#include "math/quat.h"
#include <stdbool.h>
#include <OVR_CAPI.h>
#include <OVR_CAPI_GL.h>
typedef struct {
bool isInitialized;
bool isRendering;
bool isMirrored;
bool hmdPresent;
bool needRefreshTracking;
bool needRefreshButtons;
void (*renderCallback)(void*);
vec_controller_t controllers;
ovrSession session;
ovrGraphicsLuid luid;
float clipNear;
float clipFar;
int lastButtonState;
struct {
ovrSizei size;
GLuint fboId;
GLuint depthId;
ovrTextureSwapChain chain;
} eyeTextures[2];
ovrMirrorTexture mirrorTexture;
GLuint mirrorFBO;
} HeadsetState;
static HeadsetState state;
static void ovrInit(float offset, int msaa) {
ovrResult result = ovr_Initialize(NULL);
if (OVR_FAILURE(result))
return;
result = ovr_Create(&state.session, &state.luid);
if (OVR_FAILURE(result)) {
ovr_Shutdown();
return;
}
state.needRefreshTracking = true;
state.needRefreshButtons = true;
state.lastButtonState = 0;
state.isInitialized = true;
state.isMirrored = true;
state.mirrorTexture = NULL;
int i;
for (i = 0; i < 2; i++) {
state.eyeTextures[i].size.w = 0;
state.eyeTextures[i].size.h = 0;
state.eyeTextures[i].fboId = 0;
state.eyeTextures[i].depthId = 0;
state.eyeTextures[i].chain = NULL;
}
state.clipNear = 0.1f;
state.clipFar = 30.f;
vec_init(&state.controllers);
// per the docs, ovrHand* is intended as array indices - so we use it directly.
for (i = ovrHand_Left; i < ovrHand_Count; i++) {
Controller *controller = lovrAlloc(sizeof(Controller), lovrControllerDestroy);
controller->id = ovrHand_Left+i;
vec_push(&state.controllers, controller);
}
ovr_SetTrackingOriginType(state.session, ovrTrackingOrigin_FloorLevel);
lovrEventAddPump(lovrHeadsetPoll);
atexit(lovrHeadsetDestroy);
}
static void ovrDestroy() {
int i;
Controller *controller;
vec_foreach(&state.controllers, controller, i) {
lovrRelease(&controller->ref);
}
vec_deinit(&state.controllers);
if (state.mirrorTexture) {
ovr_DestroyMirrorTexture(state.session, state.mirrorTexture);
state.mirrorTexture = NULL;
}
for (i = 0; i < 2; i++) {
if (state.eyeTextures[i].chain) {
ovr_DestroyTextureSwapChain(state.session, state.eyeTextures[i].chain);
state.eyeTextures[i].chain = NULL;
}
}
ovr_Destroy(state.session);
ovr_Shutdown();
state.isInitialized = false;
}
static void checkInput(Controller *controller, int diff, int state, ovrButton button, ControllerButton target) {
if ((diff & button) > 0) {
Event e;
if ((state & button) > 0) {
e.type = EVENT_CONTROLLER_PRESSED;
e.data.controllerpressed.controller = controller;
e.data.controllerpressed.button = target;
}
else {
e.type = EVENT_CONTROLLER_RELEASED;
e.data.controllerreleased.controller = controller;
e.data.controllerreleased.button = target;
}
lovrEventPush(e);
}
}
static ovrTrackingState *refreshTracking() {
static ovrTrackingState ts;
if (!state.needRefreshTracking) {
return &ts;
}
ovrSessionStatus status;
ovr_GetSessionStatus(state.session, &status);
if (status.ShouldRecenter) {
ovr_RecenterTrackingOrigin(state.session);
}
// get the state head and controllers are predicted to be in at display time,
// per the manual (frame timing section).
double predicted = ovr_GetPredictedDisplayTime(state.session, 0);
ts = ovr_GetTrackingState(state.session, predicted, true);
state.needRefreshTracking = false;
return &ts;
}
static ovrInputState *refreshButtons() {
static ovrInputState is;
if (!state.needRefreshButtons) {
return &is;
}
ovr_GetInputState(state.session, ovrControllerType_Touch, &is);
state.needRefreshButtons = false;
return &is;
}
int lovrHeadsetIsPresent() {
return state.hmdPresent? 1 : 0;
}
HeadsetType lovrHeadsetGetType() {
return HEADSET_RIFT;
}
HeadsetOrigin lovrHeadsetGetOriginType() {
return ORIGIN_FLOOR;
}
int lovrHeadsetIsMirrored() {
return (int)state.isMirrored;
}
void lovrHeadsetSetMirrored(int mirror) {
state.isMirrored = mirror? true : false;
}
void lovrHeadsetGetDisplayDimensions(int* width, int* height) {
ovrHmdDesc desc = ovr_GetHmdDesc(state.session);
ovrSizei size = ovr_GetFovTextureSize(state.session, ovrEye_Left, desc.DefaultEyeFov[0], 1.0f);
*width = size.w;
*height = size.h;
}
void lovrHeadsetGetClipDistance(float* near, float* far) {
if (!state.isInitialized) {
*near = *far = 0.f;
} else {
*near = state.clipNear;
*far = state.clipFar;
}
}
void lovrHeadsetSetClipDistance(float near, float far) {
if (!state.isInitialized) return;
state.clipNear = near;
state.clipFar = far;
}
float lovrHeadsetGetBoundsWidth() {
ovrVector3f dimensions;
ovr_GetBoundaryDimensions(state.session, ovrBoundary_PlayArea, &dimensions);
return dimensions.x;
}
float lovrHeadsetGetBoundsDepth() {
ovrVector3f dimensions;
ovr_GetBoundaryDimensions(state.session, ovrBoundary_PlayArea, &dimensions);
return dimensions.z;
}
void lovrHeadsetGetPosition(float* x, float* y, float* z) {
ovrTrackingState *ts = refreshTracking();
ovrVector3f pos = ts->HeadPose.ThePose.Position;
*x = pos.x;
*y = pos.y;
*z = pos.z;
}
void lovrHeadsetGetEyePosition(HeadsetEye eye, float* x, float* y, float* z) {
// we don't actually know these until render time, does this even need to be exposed?
*x = 0.0f;
*y = 0.0f;
*z = 0.0f;
}
void lovrHeadsetGetOrientation(float* angle, float* x, float* y, float* z) {
ovrTrackingState *ts = refreshTracking();
ovrQuatf oq = ts->HeadPose.ThePose.Orientation;
float quat[] = { oq.x, oq.y, oq.z, oq.w };
quat_getAngleAxis(quat, angle, x, y, z);
}
void lovrHeadsetGetVelocity(float* x, float* y, float* z) {
ovrTrackingState *ts = refreshTracking();
ovrVector3f vel = ts->HeadPose.LinearVelocity;
*x = vel.x;
*y = vel.y;
*z = vel.z;
}
void lovrHeadsetGetAngularVelocity(float* x, float* y, float* z) {
ovrTrackingState *ts = refreshTracking();
ovrVector3f vel = ts->HeadPose.AngularVelocity;
*x = vel.x;
*y = vel.y;
*z = vel.z;
}
vec_controller_t* lovrHeadsetGetControllers() {
return &state.controllers;
}
int lovrHeadsetControllerIsPresent(Controller* controller) {
ovrInputState *is = refreshButtons();
switch (controller->id) {
case ovrHand_Left: return (is->ControllerType & ovrControllerType_LTouch) == ovrControllerType_LTouch;
case ovrHand_Right: return (is->ControllerType & ovrControllerType_RTouch) == ovrControllerType_RTouch;
default: return 0;
}
return 0;
}
ControllerHand lovrHeadsetControllerGetHand(Controller* controller) {
switch (controller->id) {
case ovrHand_Left: return HAND_LEFT;
case ovrHand_Right: return HAND_RIGHT;
default: return HAND_UNKNOWN;
}
return HAND_UNKNOWN;
}
void lovrHeadsetControllerGetPosition(Controller* controller, float* x, float* y, float* z) {
ovrTrackingState *ts = refreshTracking();
ovrVector3f pos = ts->HandPoses[controller->id].ThePose.Position;
*x = pos.x;
*y = pos.y;
*z = pos.z;
}
void lovrHeadsetControllerGetOrientation(Controller* controller, float* angle, float* x, float* y, float* z) {
ovrTrackingState *ts = refreshTracking();
ovrQuatf orient = ts->HandPoses[controller->id].ThePose.Orientation;
float quat[4] = { orient.x, orient.y, orient.z, orient.w };
quat_getAngleAxis(quat, angle, x, y, z);
}
float lovrHeadsetControllerGetAxis(Controller* controller, ControllerAxis axis) {
ovrInputState *is = refreshButtons();
switch (axis) {
case CONTROLLER_AXIS_GRIP: return is->HandTriggerNoDeadzone[controller->id];
case CONTROLLER_AXIS_TRIGGER: return is->IndexTriggerNoDeadzone[controller->id];
case CONTROLLER_AXIS_TOUCHPAD_X: return is->ThumbstickNoDeadzone[controller->id].x;
case CONTROLLER_AXIS_TOUCHPAD_Y: return is->ThumbstickNoDeadzone[controller->id].y;
default: return 0.0f;
}
return 0.0f;
}
int lovrHeadsetControllerIsDown(Controller* controller, ControllerButton button) {
ovrInputState *is = refreshButtons();
int relevant = is->Buttons & ((controller->id == ovrHand_Left) ? ovrButton_LMask : ovrButton_RMask);
switch (button) {
case CONTROLLER_BUTTON_A: return (relevant & ovrButton_A) > 0;
case CONTROLLER_BUTTON_B: return (relevant & ovrButton_B) > 0;
case CONTROLLER_BUTTON_X: return (relevant & ovrButton_X) > 0;
case CONTROLLER_BUTTON_Y: return (relevant & ovrButton_Y) > 0;
case CONTROLLER_BUTTON_MENU: return (relevant & ovrButton_Enter) > 0;
case CONTROLLER_BUTTON_TRIGGER: return (relevant & (ovrButton_LShoulder | ovrButton_RShoulder)) > 0;
case CONTROLLER_BUTTON_TOUCHPAD:
case CONTROLLER_BUTTON_JOYSTICK: return (relevant & (ovrButton_LThumb | ovrButton_RThumb)) > 0;
case CONTROLLER_BUTTON_GRIP: return is->HandTrigger[controller->id] > 0.0f;
default: return 0;
}
return 0;
}
int lovrHeadsetControllerIsTouched(Controller* controller, ControllerButton button) {
ovrInputState *is = refreshButtons();
int relevant = is->Touches & ((controller->id == ovrHand_Left) ? ovrTouch_LButtonMask : ovrTouch_RButtonMask);
switch (button) {
case CONTROLLER_BUTTON_A: return (relevant & ovrTouch_A) > 0;
case CONTROLLER_BUTTON_B: return (relevant & ovrTouch_B) > 0;
case CONTROLLER_BUTTON_X: return (relevant & ovrTouch_X) > 0;
case CONTROLLER_BUTTON_Y: return (relevant & ovrTouch_Y) > 0;
case CONTROLLER_BUTTON_TRIGGER: return (relevant & (ovrTouch_LIndexTrigger | ovrTouch_RIndexTrigger)) > 0;
case CONTROLLER_BUTTON_TOUCHPAD:
case CONTROLLER_BUTTON_JOYSTICK: return (relevant & (ovrTouch_LThumb | ovrTouch_RThumb)) > 0;
default: return 0;
}
return 0;
}
void lovrHeadsetControllerVibrate(Controller* controller, float duration, float power) {
//ovr_SetControllerVibration(state.session, ovrControllerType_LTouch, freq, power);
// TODO
}
ModelData* lovrHeadsetControllerNewModelData(Controller* controller) {
// TODO
return NULL;
}
// TODO: need to set up swap chain textures for the eyes and finish view transforms
void lovrHeadsetRenderTo(headsetRenderCallback callback, void* userdata) {
if (!state.isInitialized) return;
state.renderCallback = callback;
ovrHmdDesc desc = ovr_GetHmdDesc(state.session);
if (!state.mirrorTexture) {
int i;
for (i = 0; i < 2; i++) {
state.eyeTextures[i].size = ovr_GetFovTextureSize(state.session, ovrEye_Left+i, desc.DefaultEyeFov[ovrEye_Left+i], 1.0f);
ovrTextureSwapChainDesc swdesc = {0};
swdesc.Type = ovrTexture_2D;
swdesc.ArraySize = 1;
swdesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
swdesc.Width = state.eyeTextures[i].size.w;
swdesc.Height = state.eyeTextures[i].size.h;
swdesc.MipLevels = 1;
swdesc.SampleCount = 1;
swdesc.StaticImage = ovrFalse;
ovrTextureSwapChain chain;
if (OVR_SUCCESS(ovr_CreateTextureSwapChainGL(state.session, &swdesc, &chain))) {
state.eyeTextures[i].chain = chain;
int len;
ovr_GetTextureSwapChainLength(state.session, state.eyeTextures[i].chain, &len);
int j;
for (j = 0; j < len; j++) {
GLuint texId;
ovr_GetTextureSwapChainBufferGL(state.session, state.eyeTextures[i].chain, j, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
else {
lovrAssert(chain != NULL, "Unable to create swap chain.");
break;
}
glGenFramebuffers(1, &state.eyeTextures[i].fboId);
glGenTextures(1, &state.eyeTextures[i].depthId);
glBindTexture(GL_TEXTURE_2D, state.eyeTextures[i].depthId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, state.eyeTextures[i].size.w, state.eyeTextures[i].size.h, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
}
ovrMirrorTextureDesc mdesc;
memset(&mdesc, 0, sizeof(mdesc));
mdesc.Width = lovrGraphicsGetWidth();
mdesc.Height = lovrGraphicsGetHeight();
mdesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
// Create mirror texture and an FBO used to copy mirror texture to back buffer
ovr_CreateMirrorTextureGL(state.session, &mdesc, &state.mirrorTexture);
GLuint texId;
ovr_GetMirrorTextureBufferGL(state.session, state.mirrorTexture, &texId);
glGenFramebuffers(1, &state.mirrorFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER, state.mirrorFBO);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
lovrGraphicsPushCanvas();
state.isRendering = true;
ovrEyeRenderDesc eyeRenderDesc[2];
eyeRenderDesc[0] = ovr_GetRenderDesc(state.session, ovrEye_Left, desc.DefaultEyeFov[0]);
eyeRenderDesc[1] = ovr_GetRenderDesc(state.session, ovrEye_Right, desc.DefaultEyeFov[1]);
ovrVector3f HmdToEyeOffset[2] = {
eyeRenderDesc[0].HmdToEyeOffset,
eyeRenderDesc[1].HmdToEyeOffset
};
ovrPosef EyeRenderPose[2];
double sensorSampleTime;
ovr_GetEyePoses(state.session, 0, ovrTrue, HmdToEyeOffset, EyeRenderPose, &sensorSampleTime);
float transform[16];
float projection[16];
for (HeadsetEye eye = EYE_LEFT; eye <= EYE_RIGHT; eye++) {
float orient[] = {
EyeRenderPose[eye].Orientation.x,
EyeRenderPose[eye].Orientation.y,
EyeRenderPose[eye].Orientation.z,
-EyeRenderPose[eye].Orientation.w
};
float pos[] = {
EyeRenderPose[eye].Position.x,
EyeRenderPose[eye].Position.y,
EyeRenderPose[eye].Position.z,
};
mat4_identity(transform);
mat4_rotateQuat(transform, orient);
transform[12] = -(transform[0] * pos[0] + transform[4] * pos[1] + transform[8] * pos[2]);
transform[13] = -(transform[1] * pos[0] + transform[5] * pos[1] + transform[9] * pos[2]);
transform[14] = -(transform[2] * pos[0] + transform[6] * pos[1] + transform[10] * pos[2]);
ovrTextureSwapChain chain = state.eyeTextures[eye].chain;
if (chain == NULL) {
lovrAssert(chain != NULL, "Swap chain is broken. This is a bug.");
continue;
}
int curIndex;
GLuint curTexId;
ovr_GetTextureSwapChainCurrentIndex(state.session, chain, &curIndex);
ovr_GetTextureSwapChainBufferGL(state.session, chain, curIndex, &curTexId);
glBindFramebuffer(GL_FRAMEBUFFER, state.eyeTextures[eye].fboId);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, curTexId, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, state.eyeTextures[eye].depthId, 0);
glViewport(0, 0, state.eyeTextures[eye].size.w, state.eyeTextures[eye].size.h);
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_VIEW, transform);
ovrMatrix4f proj = ovrMatrix4f_Projection(desc.DefaultEyeFov[eye], state.clipNear, state.clipFar, ovrProjection_ClipRangeOpenGL);
mat4_fromMat44(projection, proj.M);
lovrGraphicsSetProjection(projection);
lovrGraphicsClear(1, 1);
callback(eye, userdata);
lovrGraphicsPop();
glBindFramebuffer(GL_FRAMEBUFFER, state.eyeTextures[eye].fboId);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
ovr_CommitTextureSwapChain(state.session, chain);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ovrLayerEyeFov ld;
ld.Header.Type = ovrLayerType_EyeFov;
ld.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
for (int eye = 0; eye < 2; eye++) {
ld.ColorTexture[eye] = state.eyeTextures[eye].chain;
ovrRecti vp;
vp.Pos.x = 0;
vp.Pos.y = 0;
vp.Size.w = state.eyeTextures[eye].size.w;
vp.Size.h = state.eyeTextures[eye].size.h;
ld.Viewport[eye] = vp;
ld.Fov[eye] = desc.DefaultEyeFov[eye];
ld.RenderPose[eye] = EyeRenderPose[eye];
ld.SensorSampleTime = sensorSampleTime;
}
ovrLayerHeader* layers = &ld.Header;
ovr_SubmitFrame(state.session, 0, NULL, &layers, 1);
// apparently if this happens we should kill the session and reinit as long as we're getting ovrError_DisplayLost,
// lest oculus get upset should you try to get anything on the store.
// if (!OVR_SUCCESS(result))
// goto Done;
state.isRendering = false;
state.needRefreshTracking = true;
state.needRefreshButtons = true;
lovrGraphicsPopCanvas();
if (state.isMirrored || 1) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, state.mirrorFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
int w = lovrGraphicsGetWidth();
int h = lovrGraphicsGetHeight();
glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
}
static void ovrUpdate(float dt) {
ovrInputState *is = refreshButtons();
ovrSessionStatus status;
ovr_GetSessionStatus(state.session, &status);
if (status.ShouldQuit) {
Event e;
e.type = EVENT_QUIT;
e.data.quit.exitCode = 0;
lovrEventPush(e);
}
//if (!status.HmdMounted) // TODO: update mirror only?
state.hmdPresent = (status.HmdPresent == ovrTrue)? true : false;
Controller* left = state.controllers.data[ovrHand_Left];
Controller* right = state.controllers.data[ovrHand_Right];
int diff = is->Buttons ^ state.lastButtonState;
int istate = is->Buttons;
checkInput(right, diff, istate, ovrButton_A, CONTROLLER_BUTTON_A);
checkInput(right, diff, istate, ovrButton_B, CONTROLLER_BUTTON_B);
checkInput(right, diff, istate, ovrButton_RShoulder, CONTROLLER_BUTTON_TRIGGER);
checkInput(right, diff, istate, ovrButton_RThumb, CONTROLLER_BUTTON_JOYSTICK);
checkInput(left, diff, istate, ovrButton_X, CONTROLLER_BUTTON_X);
checkInput(left, diff, istate, ovrButton_Y, CONTROLLER_BUTTON_Y);
checkInput(left, diff, istate, ovrButton_LShoulder, CONTROLLER_BUTTON_TRIGGER);
checkInput(left, diff, istate, ovrButton_LThumb, CONTROLLER_BUTTON_JOYSTICK);
checkInput(left, diff, istate, ovrButton_Enter, CONTROLLER_BUTTON_MENU);
state.lastButtonState = is->Buttons;
}