mirror of https://github.com/bjornbytes/lovr.git
Leap WIP;
This commit is contained in:
parent
400598735c
commit
5ff49e73c2
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue