1
0
Fork 0
mirror of https://github.com/bjornbytes/lovr.git synced 2024-07-05 05:53:33 +00:00
lovr/src/headset/oculus.c
bjorn 7807b1abd7 lovr.headset.getAcceleration, getAngularAcceleration;
Add missing getBonePose impls.

Refactor some ovr stuff.
2019-05-12 22:23:02 -07:00

443 lines
14 KiB
C

#include "headset/headset.h"
#include "event/event.h"
#include "graphics/graphics.h"
#include "graphics/canvas.h"
#include "graphics/texture.h"
#include "lib/maf.h"
#include "lib/vec/vec.h"
#include <stdbool.h>
#include <OVR_CAPI.h>
#include <OVR_CAPI_GL.h>
static struct {
bool needRefreshTracking;
bool needRefreshButtons;
ovrSession session;
ovrGraphicsLuid luid;
float clipNear;
float clipFar;
float offset;
ovrSizei size;
Canvas* canvas;
ovrTextureSwapChain chain;
ovrMirrorTexture mirror;
map_t(Texture*) textureLookup;
} state;
static Texture* lookupTexture(uint32_t handle) {
char key[4 + 1] = { 0 };
lovrAssert(handle < 9999, "Texture handle overflow");
sprintf(key, "%d", handle);
Texture** texture = map_get(&state.textureLookup, key);
if (!texture) {
map_set(&state.textureLookup, key, lovrTextureCreateFromHandle(handle, TEXTURE_2D));
texture = map_get(&state.textureLookup, key);
}
return *texture;
}
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;
}
static bool oculus_init(float offset, int msaa) {
ovrResult result = ovr_Initialize(NULL);
if (OVR_FAILURE(result)) {
return false;
}
result = ovr_Create(&state.session, &state.luid);
if (OVR_FAILURE(result)) {
ovr_Shutdown();
return false;
}
state.needRefreshTracking = true;
state.needRefreshButtons = true;
state.clipNear = 0.1f;
state.clipFar = 30.f;
state.offset = offset;
map_init(&state.textureLookup);
ovr_SetTrackingOriginType(state.session, ovrTrackingOrigin_FloorLevel);
return true;
}
static void oculus_destroy() {
const char* key;
map_iter_t iter = map_iter(&state.textureLookup);
while ((key = map_next(&state.textureLookup, &iter)) != NULL) {
Texture* texture = *map_get(&state.textureLookup, key);
lovrRelease(Texture, texture);
}
map_deinit(&state.textureLookup);
if (state.mirror) {
ovr_DestroyMirrorTexture(state.session, state.mirror);
state.mirror = NULL;
}
if (state.chain) {
ovr_DestroyTextureSwapChain(state.session, state.chain);
state.chain = NULL;
}
lovrRelease(Canvas, state.canvas);
ovr_Destroy(state.session);
ovr_Shutdown();
memset(&state, 0, sizeof(state));
}
static bool oculus_getName(char* name, size_t length) {
ovrHmdDesc desc = ovr_GetHmdDesc(state.session);
strncpy(name, desc.ProductName, length - 1);
name[length - 1] = '\0';
return true;
}
static HeadsetOrigin oculus_getOriginType() {
return ORIGIN_FLOOR;
}
static void oculus_getDisplayDimensions(uint32_t* width, uint32_t* 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;
}
static double oculus_getDisplayTime(void) {
return ovr_GetPredictedDisplayTime(state.session, 0);
}
static void oculus_getClipDistance(float* clipNear, float* clipFar) {
*clipNear = state.clipNear;
*clipFar = state.clipFar;
}
static void oculus_setClipDistance(float clipNear, float clipFar) {
state.clipNear = clipNear;
state.clipFar = clipFar;
}
static void oculus_getBoundsDimensions(float* width, float* depth) {
ovrVector3f dimensions;
ovr_GetBoundaryDimensions(state.session, ovrBoundary_PlayArea, &dimensions);
*width = dimensions.x;
*depth = dimensions.z;
}
static const float* oculus_getBoundsGeometry(int* count) {
*count = 0;
return NULL;
}
ovrPoseStatef* getPose(Device device) {
ovrTrackingState *ts = refreshTracking();
switch (device) {
case DEVICE_HEAD: return &ts->HeadPose;
case DEVICE_HAND_LEFT: return &ts->HandPoses[ovrHand_Left];
case DEVICE_HAND_RIGHT: return &ts->HandPoses[ovrHand_Right];
default: return NULL;
}
}
static bool oculus_getPose(Device device, vec3 position, quat orientation) {
ovrPoseStatef* poseState = getPose(device);
if (!poseState) return false;
ovrPosef* pose = poseState->ThePose;
if (position) {
vec3_set(position, pose->Position.x, pose->Position.y + state.offset, pose->Position.z);
}
if (orientation) {
quat_set(orientation, pose->Orientation.x, pose->Orientation.y, pose->Orientation.z, pose->Orientation.w);
}
return true;
}
static bool oculus_getBonePose(Device device, DeviceBone bone, vec3 position, quat orientation) {
return false;
}
static bool oculus_getVelocity(const char* path, vec3 velocity, vec3 angularVelocity) {
ovrPoseStatef* pose = getPose(device);
if (!pose) return false;
if (velocity) {
vec3_set(velocity, pose->LinearVelocity.x, pose->LinearVelocity.y, pose->LinearVelocity.z);
}
if (angularVelocity) {
vec3_set(angularVelocity, pose->AngularVelocity.x, pose->AngularVelocity.y, pose->AngularVelocity.z);
}
return true;
}
static bool oculus_getAcceleration(Device device, vec3 acceleration, vec3 angularAcceleration) {
ovrPoseStatef* pose = getPose(device);
if (!pose) return false;
if (acceleration) {
vec3_set(acceleration, pose->LinearAcceleration.x, pose->LinearAcceleration.y, pose->LinearAcceleration.z);
}
if (angularAcceleration) {
vec3_set(angularAcceleration, pose->AngularAcceleration.x, pose->AngularAcceleration.y, pose->AngularAcceleration.z);
}
return true;
}
static bool oculus_isDown(Device device, DeviceButton button, bool* down) {
if (device == DEVICE_HEAD && button == BUTTON_PROXIMITY) {
ovrSessionStatus status;
ovr_GetSessionStatus(state.session, &status);
*down = status.HmdMounted;
return true;
} else if (device != DEVICE_HAND_LEFT && device != DEVICE_HAND_RIGHT) {
return false;
}
ovrInputState* is = refreshButtons();
ovrHand hand = device == DEVICE_HAND_LEFT ? ovrHand_Left : ovrHand_Right;
uint32_t buttons = is->Buttons & (device == DEVICE_HAND_LEFT ? ovrButton_LMask : ovrButton_RMask);
switch (button) {
case BUTTON_A: return *down = (buttons & ovrButton_A), true;
case BUTTON_B: return *down = (buttons & ovrButton_B), true;
case BUTTON_X: return *down = (buttons & ovrButton_X), true;
case BUTTON_Y: return *down = (buttons & ovrButton_Y), true;
case BUTTON_MENU: return *down = (buttons & ovrButton_Enter), true;
case BUTTON_TRIGGER: return *down = (is->IndexTriggerNoDeadzone[hand] > .5f), true;
case BUTTON_THUMBSTICK: return *down = (buttons & (ovrButton_LThumb | ovrButton_RThumb), true;
case BUTTON_GRIP: return *down = (masks->HandTrigger[hand] > .9f), true;
default: return false;
}
}
static bool oculus_isTouched(Device device, DeviceButton button, bool* touched) {
if (device != DEVICE_HAND_LEFT && device != DEVICE_HAND_RIGHT) {
return false;
}
ovrInputState* is = refreshButtons();
ovrHand hand = device == DEVICE_HAND_LEFT ? ovrHand_Left : ovrHand_Right;
uint32_t touches = is->Touches & (device == DEVICE_HAND_LEFT ? ovrTouch_LButtonMask : ovrTouch_RButtonMask);
switch (button) {
case BUTTON_A: return *touched = (touches & ovrTouch_A), true;
case BUTTON_B: return *touched = (touches & ovrTouch_B), true;
case BUTTON_X: return *touched = (touches & ovrTouch_X), true;
case BUTTON_Y: return *touched = (touches & ovrTouch_Y), true;
case BUTTON_TRIGGER: return *touched = (touches & (ovrTouch_LIndexTrigger | ovrTouch_RIndexTrigger)), true;
case BUTTON_THUMBSTICK: return *touched = (touches & (ovrTouch_LThumb | ovrTouch_RThumb)), true;
default: return false;
}
}
static bool oculus_getAxis(Device device, DeviceAxis axis, float* value) {
if (device != DEVICE_HAND_LEFT && device != DEVICE_HAND_RIGHT) {
return false;
}
ovrInputState* is = refreshButtons();
ovrHand hand = device == DEVICE_HAND_LEFT ? ovrHand_Left : ovrHand_Right;
switch (axis) {
case AXIS_GRIP: return *value = is->HandTriggerNoDeadzone[hand], true;
case AXIS_TRIGGER: return *value = is->IndexTriggerNoDeadzone[hand], true;
case AXIS_THUMBSTICK_X: return *value = is->ThumbstickNoDeadzone[hand].x, true;
case AXIS_THUMBSTICK_Y: return *value = is->ThumbstickNoDeadzone[hand].y, true;
default: return false;
}
}
static bool oculus_vibrate(Device device, float strength, float duration, float frequency) {
return false; // TODO
}
static ModelData* oculus_newModelData(Device device) {
return NULL; // TODO
}
static void oculus_renderTo(void (*callback)(void*), void* userdata) {
ovrHmdDesc desc = ovr_GetHmdDesc(state.session);
if (!state.canvas) {
state.size = ovr_GetFovTextureSize(state.session, ovrEye_Left, desc.DefaultEyeFov[ovrEye_Left], 1.0f);
ovrTextureSwapChainDesc swdesc = {
.Type = ovrTexture_2D,
.ArraySize = 1,
.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB,
.Width = 2 * state.size.w,
.Height = state.size.h,
.MipLevels = 1,
.SampleCount = 1,
.StaticImage = ovrFalse
};
lovrAssert(OVR_SUCCESS(ovr_CreateTextureSwapChainGL(state.session, &swdesc, &state.chain)), "Unable to create swapchain");
ovrMirrorTextureDesc mdesc = {
.Width = lovrGraphicsGetWidth(),
.Height = lovrGraphicsGetHeight(),
.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB,
.MirrorOptions = ovrMirrorOption_PostDistortion
};
lovrAssert(OVR_SUCCESS(ovr_CreateMirrorTextureWithOptionsGL(state.session, &mdesc, &state.mirror)), "Unable to create mirror texture");
CanvasFlags flags = { .depth = { .enabled = true, .format = FORMAT_D24S8 }, .stereo = true };
state.canvas = lovrCanvasCreate(2 * state.size.w, state.size.h, flags);
}
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]);
ovrPosef HmdToEyeOffset[2] = {
eyeRenderDesc[0].HmdToEyePose,
eyeRenderDesc[1].HmdToEyePose
};
ovrPosef EyeRenderPose[2];
double sensorSampleTime;
ovr_GetEyePoses(state.session, 0, ovrTrue, HmdToEyeOffset, EyeRenderPose, &sensorSampleTime);
Camera camera = { .canvas = state.canvas };
for (int eye = 0; eye < 2; 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 transform = camera.viewMatrix[eye];
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]);
ovrMatrix4f projection = ovrMatrix4f_Projection(desc.DefaultEyeFov[eye], state.clipNear, state.clipFar, ovrProjection_ClipRangeOpenGL);
mat4_fromMat44(camera.projection[eye], projection.M);
}
int curIndex;
uint32_t curTexId;
ovr_GetTextureSwapChainCurrentIndex(state.session, state.chain, &curIndex);
ovr_GetTextureSwapChainBufferGL(state.session, state.chain, curIndex, &curTexId);
Texture* texture = lookupTexture(curTexId);
lovrCanvasSetAttachments(state.canvas, &(Attachment) { texture, 0, 0 }, 1);
lovrGraphicsSetCamera(&camera, true);
callback(userdata);
lovrGraphicsSetCamera(NULL, false);
ovr_CommitTextureSwapChain(state.session, state.chain);
ovrLayerEyeFov ld;
ld.Header.Type = ovrLayerType_EyeFov;
ld.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
for (int eye = 0; eye < 2; eye++) {
ld.ColorTexture[eye] = state.chain;
ovrRecti vp;
vp.Pos.x = state.size.w * eye;
vp.Pos.y = 0;
vp.Size.w = state.size.w;
vp.Size.h = state.size.h;
ld.Viewport[eye] = vp;
ld.Fov[eye] = desc.DefaultEyeFov[eye];
ld.RenderPose[eye] = EyeRenderPose[eye];
ld.SensorSampleTime = sensorSampleTime;
}
const ovrLayerHeader* layers = &ld.Header;
ovr_SubmitFrame(state.session, 0, NULL, &layers, 1);
state.needRefreshTracking = true;
state.needRefreshButtons = true;
}
static Texture* oculus_getMirrorTexture() {
uint32_t handle;
ovr_GetMirrorTextureBufferGL(state.session, state.mirror, &handle);
return lookupTexture(handle);
}
static void oculus_update(float dt) {
ovrSessionStatus status;
ovr_GetSessionStatus(state.session, &status);
if (status.ShouldQuit) {
Event e;
e.type = EVENT_QUIT;
e.data.quit.exitCode = 0;
lovrEventPush(e);
}
}
HeadsetInterface lovrHeadsetOculusDriver = {
.driverType = DRIVER_OCULUS,
.init = oculus_init,
.destroy = oculus_destroy,
.getName = oculus_getName,
.getOriginType = oculus_getOriginType,
.getDisplayDimensions = oculus_getDisplayDimensions,
.getDisplayTime = oculus_getDisplayTime,
.getClipDistance = oculus_getClipDistance,
.setClipDistance = oculus_setClipDistance,
.getBoundsDimensions = oculus_getBoundsDimensions,
.getBoundsGeometry = oculus_getBoundsGeometry,
.getPose = oculus_getPose,
.getBonePose = oculus_getBonePose,
.getVelocity = oculus_getVelocity,
.getAcceleration = oculus_getAcceleration,
.isDown = oculus_isDown,
.isTouched = oculus_isTouched,
.getAxis = oculus_getAxis,
.vibrate = oculus_vibrate,
.newModelData = oculus_newModelData,
.renderTo = oculus_renderTo,
.getMirrorTexture = oculus_getMirrorTexture,
.update = oculus_update
};