Leap WIP;

This commit is contained in:
bjorn 2019-04-09 12:38:55 -07:00
parent 400598735c
commit 5ff49e73c2
6 changed files with 258 additions and 4 deletions

View File

@ -23,6 +23,7 @@ option(LOVR_USE_WEBVR "Enable the WebVR backend for the headset module" OFF)
option(LOVR_USE_OCULUS "Enable the LibOVR backend for the headset module (be sure to also set LOVR_OCULUS_PATH to point to the Oculus SDK)" OFF)
option(LOVR_USE_OCULUS_MOBILE "Enable the Oculus Mobile (Android) backend for the headset module" OFF)
option(LOVR_USE_DESKTOP_HEADSET "Enable the keyboard/mouse backend for the headset module" ON)
option(LOVR_USE_LEAP_MOTION "Enable the Leap Motion backend for the headset module" ON)
option(LOVR_USE_SSE "Enable SIMD use of intrinsics" ON)
option(LOVR_SYSTEM_PHYSFS "Use the system-provided PhysFS" OFF)
@ -270,10 +271,27 @@ if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENVR)
)
endif()
# Leap Motion
if(LOVR_ENABLE_HEADSET AND LOVR_USE_LEAP_MOTION)
if(NOT LOVR_LEAP_PATH)
message(FATAL_ERROR "LOVR_USE_LEAP_MOTION requires the LOVR_LEAP_PATH to be set to the location of the Leap Motion SDK (LeapSDK) folder")
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LEAP_ARCH "x64")
else()
set(LEAP_ARCH "x86")
endif()
add_library(LeapC SHARED IMPORTED)
include_directories("${LOVR_LEAP_PATH}/include")
set_target_properties(LeapC PROPERTIES IMPORTED_IMPLIB "${LOVR_LEAP_PATH}/lib/${LEAP_ARCH}/LeapC.lib")
set_target_properties(LeapC PROPERTIES IMPORTED_LOCATION "${LOVR_LEAP_PATH}/lib/${LEAP_ARCH}/LeapC.dll")
set(LOVR_LEAP LeapC)
endif()
# Oculus SDK -- expects Oculus SDK 1.26.0 or later
if (LOVR_ENABLE_HEADSET AND LOVR_USE_OCULUS)
if(LOVR_ENABLE_HEADSET AND LOVR_USE_OCULUS)
if(NOT LOVR_OCULUS_PATH)
message(FATAL_ERROR "LOVR_USE_OCULUS requires the LOVR_OCULUS_PATH to be set to the location of the Oculus Desktop SDK")
message(FATAL_ERROR "LOVR_USE_OCULUS requires the LOVR_OCULUS_PATH to be set to the location of the Oculus Desktop SDK (LibOVR) folder")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(OCULUS_BUILD_TYPE "Release")
@ -333,6 +351,7 @@ target_link_libraries(lovr
${LOVR_OPENGL}
${LOVR_OPENVR}
${LOVR_OCULUS}
${LOVR_LEAP}
${LOVR_PHYSFS}
${LOVR_PTHREADS}
${LOVR_EMSCRIPTEN_FLAGS}
@ -449,6 +468,10 @@ if(LOVR_ENABLE_HEADSET)
add_definitions(-DLOVR_USE_DESKTOP_HEADSET)
target_sources(lovr PRIVATE src/headset/desktop.c)
endif()
if(LOVR_USE_LEAP_MOTION)
add_definitions(-DLOVR_USE_LEAP_MOTION)
target_sources(lovr PRIVATE src/headset/leap.c)
endif()
endif()
if(LOVR_ENABLE_MATH)
@ -545,6 +568,7 @@ if(WIN32)
move_dll(${LOVR_OPENAL})
move_dll(${LOVR_OPENVR})
move_dll(${LOVR_PHYSFS})
move_dll(${LOVR_LEAP})
target_compile_definitions(lovr PRIVATE -DLOVR_GL)
elseif(APPLE)
target_sources(lovr PRIVATE src/platform/macos.c)

View File

@ -12,6 +12,7 @@
const char* HeadsetDrivers[] = {
[DRIVER_DESKTOP] = "desktop",
[DRIVER_LEAP_MOTION] = "leap",
[DRIVER_OCULUS] = "oculus",
[DRIVER_OCULUS_MOBILE] = "oculusmobile",
[DRIVER_OPENVR] = "openvr",
@ -349,8 +350,14 @@ static int l_lovrHeadsetRenderTo(lua_State* L) {
}
static int l_lovrHeadsetUpdate(lua_State* L) {
float dt = luax_checkfloat(L, 1);
if (lovrHeadsetDriver->update) {
lovrHeadsetDriver->update(luax_checkfloat(L, 1));
lovrHeadsetDriver->update(dt);
}
FOREACH_TRACKING_DRIVER(driver) {
driver->update(dt);
}
return 0;

View File

@ -18,6 +18,9 @@ bool lovrHeadsetInit(HeadsetDriver* drivers, int count, float offset, int msaa)
#ifdef LOVR_USE_DESKTOP_HEADSET
case DRIVER_DESKTOP: interface = &lovrHeadsetDesktopDriver; break;
#endif
#ifdef LOVR_USE_LEAP_MOTION
case DRIVER_LEAP_MOTION: interface = &lovrHeadsetLeapMotionDriver; break;
#endif
#ifdef LOVR_USE_OCULUS
case DRIVER_OCULUS: interface = &lovrHeadsetOculusDriver; break;
#endif

View File

@ -15,6 +15,7 @@ typedef enum {
typedef enum {
DRIVER_DESKTOP,
DRIVER_LEAP_MOTION,
DRIVER_OCULUS,
DRIVER_OCULUS_MOBILE,
DRIVER_OPENVR,
@ -29,6 +30,7 @@ typedef struct HeadsetInterface {
bool (*getName)(char* name, size_t length);
HeadsetOrigin (*getOriginType)(void);
void (*getDisplayDimensions)(uint32_t* width, uint32_t* height);
double (*getDisplayTime)();
void (*getClipDistance)(float* clipNear, float* clipFar);
void (*setClipDistance)(float clipNear, float clipFar);
void (*getBoundsDimensions)(float* width, float* depth);
@ -51,6 +53,7 @@ extern HeadsetInterface lovrHeadsetOpenVRDriver;
extern HeadsetInterface lovrHeadsetWebVRDriver;
extern HeadsetInterface lovrHeadsetDesktopDriver;
extern HeadsetInterface lovrHeadsetOculusMobileDriver;
extern HeadsetInterface lovrHeadsetLeapMotionDriver;
// Active drivers
extern HeadsetInterface* lovrHeadsetDriver;

205
src/headset/leap.c Normal file
View File

@ -0,0 +1,205 @@
#include "headset/headset.h"
#include "platform.h"
#include "util.h"
#include "lib/maf.h"
#include "lib/tinycthread/tinycthread.h"
#include <LeapC.h>
#include <stdlib.h>
#include <inttypes.h>
static struct {
LEAP_CONNECTION connection;
LEAP_CLOCK_REBASER clock;
LEAP_TRACKING_EVENT* frame;
uint64_t frameSize;
thrd_t thread;
bool connected;
} state;
static int loop(void* userdata) {
LEAP_CONNECTION_MESSAGE message;
while (1) {
if (LeapPollConnection(state.connection, 1000, &message) == eLeapRS_Success) {
switch (message.type) {
case eLeapEventType_Connection:
state.connected = true;
LeapSetPolicyFlags(state.connection, eLeapPolicyFlag_OptimizeHMD, 0);
break;
case eLeapEventType_ConnectionLost:
state.connected = false;
break;
}
}
}
return 0;
}
static void destroy(void);
static bool init(float offset, int msaa) {
if (LeapCreateConnection(NULL, &state.connection) == eLeapRS_Success) {
if (LeapOpenConnection(state.connection) == eLeapRS_Success) {
LeapCreateClockRebaser(&state.clock);
if (thrd_create(&state.thread, loop, NULL) == thrd_success) {
return true;
}
}
}
return destroy(), false;
}
static void destroy(void) {
free(state.frame);
thrd_detach(state.thread);
LeapDestroyClockRebaser(state.clock);
LeapCloseConnection(state.connection);
LeapDestroyConnection(state.connection);
memset(&state, 0, sizeof(state));
}
static bool getPose(const char* path, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
if (!state.frame) {
return false;
}
eLeapHandType handType;
if (!strncmp("hand/left", path, strlen("hand/left"))) {
handType = eLeapHandType_Left;
path += strlen("hand/left");
} else if (!strncmp("hand/right", path, strlen("hand/right"))) {
handType = eLeapHandType_Right;
path += strlen("hand/right");
} else {
return false;
}
for (uint32_t i = 0; i < state.frame->nHands; i++) {
LEAP_HAND* hand = &state.frame->pHands[i];
if (hand->type != handType) {
continue;
}
float orientation[4];
if (*path == '\0') {
*x = hand->palm.position.x;
*y = hand->palm.position.y;
*z = hand->palm.position.z;
quat_init(orientation, hand->palm.orientation.v);
} else if (!strcmp(path, "/palm")) {
*x = hand->palm.position.x;
*y = hand->palm.position.y;
*z = hand->palm.position.z;
quat_between(orientation, (float[3]) { 0.f, 0.f, -1.f }, hand->palm.normal.v);
} else if (!strncmp("/finger/", path, strlen("/finger/"))) {
path += strlen("/finger/");
int fingerIndex;
if (!strncmp("thumb", path, strlen("thumb"))) { fingerIndex = 0; path += strlen("thumb"); }
else if (!strncmp("index", path, strlen("index"))) { fingerIndex = 1; path += strlen("index"); }
else if (!strncmp("middle", path, strlen("middle"))) { fingerIndex = 2; path += strlen("middle"); }
else if (!strncmp("ring", path, strlen("ring"))) { fingerIndex = 3; path += strlen("ring"); }
else if (!strncmp("pinky", path, strlen("pinky"))) { fingerIndex = 4; path += strlen("pinky"); }
else { return false; }
LEAP_DIGIT* finger = &hand->digits[fingerIndex];
vec3 base;
vec3 tip;
if (*path == '\0') {
tip = finger->distal.next_joint.v;
base = finger->distal.prev_joint.v;
*x = tip[0];
*y = tip[1];
*z = tip[2];
} else if (!strncmp("/bone/", path, strlen("/bone/"))) {
path += strlen("/bone/");
int boneIndex;
if (!strcmp(path, "metacarpal")) { boneIndex = 0; }
else if (!strcmp(path, "proximal")) { boneIndex = 1; }
else if (!strcmp(path, "intermediate")) { boneIndex = 2; }
else if (!strcmp(path, "distal")) { boneIndex = 3; }
else { return false; }
LEAP_BONE* bone = &finger->bones[boneIndex];
tip = bone->next_joint.v;
base = bone->prev_joint.v;
*x = base[0];
*y = base[1];
*z = base[2];
} else {
return false;
}
float direction[3];
vec3_sub(vec3_init(direction, tip), base);
quat_between(orientation, (float[3]) { 0.f, 0.f, -1.f }, direction);
}
float hx, hy, hz, hangle, hax, hay, haz;
float head[16] = MAT4_IDENTITY;
// A lot of the matrix stuff here could be cached
if (lovrHeadsetDriver && lovrHeadsetDriver->getPose("head", &hx, &hy, &hz, &hangle, &hax, &hay, &haz)) {
mat4_translate(head, hx, hy, hz);
mat4_rotate(head, hangle, hax, hay, haz);
}
float remap[16] = { -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, -80.f, 1.f };
mat4_scale(head, .001f, .001f, .001f);
mat4_multiply(head, remap);
mat4_transform(head, x, y, z);
mat4_rotateQuat(head, orientation);
quat_fromMat4(orientation, head);
quat_getAngleAxis(orientation, angle, ax, ay, az);
return true;
}
return false;
}
static bool getVelocity(const char* path, float* vx, float* vy, float* vz, float* vax, float* vay, float* vaz) {
return false;
}
static void update(float dt) {
if (!state.connected) {
return;
}
double relativePredictedDisplayTime = lovrHeadsetDriver ? lovrHeadsetDriver->getDisplayTime() : 0.;
int64_t now = (int64_t) ((lovrPlatformGetTime() * (double) 1e6) + .5);
int64_t predicted = now + (int64_t) ((relativePredictedDisplayTime * (double) 1e6) + .5);
LeapUpdateRebase(state.clock, now, LeapGetNow());
int64_t targetTime;
LeapRebaseClock(state.clock, predicted, &targetTime);
uint64_t size;
if (LeapGetFrameSize(state.connection, targetTime, &size) == eLeapRS_Success) {
if (state.frameSize < size) {
lovrAssert(size <= SIZE_MAX, "Out of memory");
state.frameSize = size;
state.frame = realloc(state.frame, (size_t) state.frameSize);
lovrAssert(state.frame, "Out of memory");
}
LeapInterpolateFrame(state.connection, targetTime, state.frame, size);
}
}
HeadsetInterface lovrHeadsetLeapMotionDriver = {
.driverType = DRIVER_LEAP_MOTION,
.init = init,
.destroy = destroy,
.getPose = getPose,
.getVelocity = getVelocity,
.update = update
};

View File

@ -133,6 +133,17 @@ static void getDisplayDimensions(uint32_t* width, uint32_t* height) {
state.system->GetRecommendedRenderTargetSize(width, height);
}
static double getDisplayTime() {
float secondsSinceVsync;
state.system->GetTimeSinceLastVsync(&secondsSinceVsync, NULL);
float frequency = state.system->GetFloatTrackedDeviceProperty(HEADSET, ETrackedDeviceProperty_Prop_DisplayFrequency_Float, NULL);
float frameDuration = 1.f / frequency;
float vsyncToPhotons = state.system->GetFloatTrackedDeviceProperty(HEADSET, ETrackedDeviceProperty_Prop_SecondsFromVsyncToPhotons_Float, NULL);
return (double) (frameDuration - secondsSinceVsync + vsyncToPhotons);
}
static void getClipDistance(float* clipNear, float* clipFar) {
*clipNear = state.clipNear;
*clipFar = state.clipFar;
@ -179,7 +190,7 @@ static bool getTransform(unsigned int device, mat4 transform) {
static bool getPose(const char* path, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
float transform[16];
TrackedDeviceIndex_t device = pathToDevice(path, NULL);
if (device == INVALID_DEVICE && !getTransform(device, transform)) {
if (device == INVALID_DEVICE || !getTransform(device, transform)) {
return false;
}
@ -451,6 +462,7 @@ HeadsetInterface lovrHeadsetOpenVRDriver = {
.getName = getName,
.getOriginType = getOriginType,
.getDisplayDimensions = getDisplayDimensions,
.getDisplayTime = getDisplayTime,
.getClipDistance = getClipDistance,
.setClipDistance = setClipDistance,
.getBoundsDimensions = getBoundsDimensions,