Start WebVR rewrite;

This commit is contained in:
bjorn 2018-04-29 14:38:08 -07:00 committed by bjornbytes
parent c767e9c165
commit 859251d020
7 changed files with 187 additions and 279 deletions

View File

@ -12,8 +12,9 @@ if(EMSCRIPTEN)
"-s USE_GLFW=3 "
"-s USE_ZLIB=1 "
"-s ALLOW_MEMORY_GROWTH=1 "
"-s EXTRA_EXPORTED_RUNTIME_METHODS=['getValue','setValue'] "
"-s WASM=1"
"-s \"EXTRA_EXPORTED_RUNTIME_METHODS=['getValue','setValue']\" "
"-s WASM=1 "
"--js-library ../src/resources/lovr.js"
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}")

View File

@ -180,18 +180,24 @@ int l_lovrHeadsetSetClipDistance(lua_State* L) {
}
int l_lovrHeadsetGetBoundsWidth(lua_State* L) {
lua_pushnumber(L, lovrHeadsetDriver->getBoundsWidth());
float width, depth;
lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, width);
return 1;
}
int l_lovrHeadsetGetBoundsDepth(lua_State* L) {
lua_pushnumber(L, lovrHeadsetDriver->getBoundsDepth());
float width, depth;
lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, depth);
return 1;
}
int l_lovrHeadsetGetBoundsDimensions(lua_State* L) {
lua_pushnumber(L, lovrHeadsetDriver->getBoundsWidth());
lua_pushnumber(L, lovrHeadsetDriver->getBoundsDepth());
float width, depth;
lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, width);
lua_pushnumber(L, depth);
return 2;
}

View File

@ -216,12 +216,8 @@ static void fakeSetClipDistance(float clipNear, float clipFar) {
state.clipFar = clipFar;
}
static float fakeGetBoundsWidth() {
return 0.0f;
}
static float fakeGetBoundsDepth() {
return 0.0f;
static void fakeGetBoundsDimensions(float* width, float* depth) {
*width = *depth = 0.f;
}
static void fakeGetPose(float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
@ -393,8 +389,7 @@ HeadsetInterface lovrHeadsetFakeDriver = {
fakeGetDisplayDimensions,
fakeGetClipDistance,
fakeSetClipDistance,
fakeGetBoundsWidth,
fakeGetBoundsDepth,
fakeGetBoundsDimensions,
fakeGetPose,
fakeGetEyePose,
fakeGetVelocity,

View File

@ -75,8 +75,7 @@ typedef struct {
void (*getDisplayDimensions)(int* width, int* height);
void (*getClipDistance)(float* clipNear, float* clipFar);
void (*setClipDistance)(float clipNear, float clipFar);
float (*getBoundsWidth)();
float (*getBoundsDepth)();
void (*getBoundsDimensions)(float* width, float* depth);
void (*getPose)(float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az);
void (*getEyePose)(HeadsetEye eye, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az);
void (*getVelocity)(float* x, float* y, float* z);

View File

@ -394,16 +394,8 @@ static void openvrSetClipDistance(float clipNear, float clipFar) {
state.clipFar = clipFar;
}
static float openvrGetBoundsWidth() {
float width;
state.chaperone->GetPlayAreaSize(&width, NULL);
return width;
}
static float openvrGetBoundsDepth() {
float depth;
state.chaperone->GetPlayAreaSize(NULL, &depth);
return depth;
static void openvrGetBoundsDimensions(float* width, float* depth) {
state.chaperone->GetPlayAreaSize(width, depth);
}
static void openvrGetPose(float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
@ -751,8 +743,7 @@ HeadsetInterface lovrHeadsetOpenVRDriver = {
openvrGetDisplayDimensions,
openvrGetClipDistance,
openvrSetClipDistance,
openvrGetBoundsWidth,
openvrGetBoundsDepth,
openvrGetBoundsDimensions,
openvrGetPose,
openvrGetEyePose,
openvrGetVelocity,

View File

@ -1,279 +1,59 @@
#include "headset/headset.h"
#include "graphics/graphics.h"
#include "lib/vec/vec.h"
#include "math/mat4.h"
#include "math/quat.h"
#include <emscripten.h>
#include <emscripten/vr.h>
#include <stdbool.h>
extern void webvrInit(void);
extern HeadsetOrigin webvrGetOriginType(void);
extern bool webvrIsMirrored(void);
extern void webvrSetMirrored(bool mirror);
extern void webvrGetDisplayDimensions(int32_t* width, int32_t* height);
extern void webvrGetClipDistance(float* near, float* far);
extern void webvrSetClipDistance(float near, float far);
extern void webvrGetBoundsDimensions(float* width, float* depth);
extern void webvrRenderTo(void (*callback)(void*), void* userdata);
typedef struct {
void (*renderCallback)(void*);
vec_controller_t controllers;
Canvas* canvas;
float offset;
} HeadsetState;
static HeadsetState state;
static void onRequestAnimationFrame(void* userdata) {
int width = emscripten_vr_get_display_width();
int height = emscripten_vr_get_display_height();
if (!state.canvas) {
CanvasFlags flags = { .msaa = 0, .depth = true, .stencil = true, .stereo = true, .mipmaps = false };
state.canvas = lovrCanvasCreate(width, height, FORMAT_RGB, flags);
}
Layer layer = { .canvas = state.canvas };
float sittingToStanding[16];
mat4_set(sittingToStanding, emscripten_vr_get_sitting_to_standing_matrix());
if (!emscripten_vr_has_stage()) {
mat4_translate(sittingToStanding, 0, state.offset, 0);
}
mat4_invert(sittingToStanding);
mat4_set(&layer.projections[0], emscripten_vr_get_projection_matrix(0));
mat4_set(&layer.projections[16], emscripten_vr_get_projection_matrix(1));
mat4_set(&layer.views[0], emscripten_vr_get_view_matrix(0));
mat4_multiply(&layer.views[0], sittingToStanding);
mat4_set(&layer.views[16], emscripten_vr_get_view_matrix(1));
mat4_multiply(&layer.views[16], sittingToStanding);
lovrGraphicsPushLayer(layer);
lovrGraphicsClear(true, true, true, lovrGraphicsGetBackgroundColor(), 1., 0);
state.renderCallback(userdata);
lovrGraphicsPopLayer();
}
static bool webvrInit(float offset) {
if (!emscripten_vr_is_present()) return false;
vec_init(&state.controllers);
emscripten_vr_init();
static bool webvrDriverInit(float offset) {
state.offset = offset;
return true;
}
static void webvrDestroy() {
Controller* controller;
int i;
vec_foreach(&state.controllers, controller, i) {
lovrRelease(controller);
}
vec_deinit(&state.controllers);
lovrRelease(state.canvas);
static void webvrDriverDestroy() {
memset(&state, 0, sizeof(HeadsetState));
}
static HeadsetType webvrGetType() {
return HEADSET_UNKNOWN;
}
static HeadsetOrigin webvrGetOriginType() {
return emscripten_vr_has_stage() ? ORIGIN_FLOOR : ORIGIN_HEAD;
}
static bool webvrIsMounted() {
return true;
}
static bool webvrIsMirrored() {
return true;
}
static void webvrSetMirrored(bool mirror) {
//
}
static void webvrGetDisplayDimensions(int* width, int* height) {
*width = emscripten_vr_get_display_width() / 2;
*height = emscripten_vr_get_display_height();
}
static void webvrGetClipDistance(float* near, float* far) {
emscripten_vr_get_display_clip_distance(near, far);
}
static void webvrSetClipDistance(float near, float far) {
emscripten_vr_set_display_clip_distance(near, far);
}
static float webvrGetBoundsWidth() {
return emscripten_vr_get_bounds_width();
}
static float webvrGetBoundsDepth() {
return emscripten_vr_get_bounds_depth();
}
static void webvrGetPose(float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
float v[3];
emscripten_vr_get_position(&v[0], &v[1], &v[2]);
mat4_transform(emscripten_vr_get_sitting_to_standing_matrix(), v);
*x = v[0];
*y = v[1];
*z = v[2];
float quat[4];
float m[16];
emscripten_vr_get_orientation(&quat[0], &quat[1], &quat[2], &quat[3]);
mat4_multiply(mat4_identity(m), emscripten_vr_get_sitting_to_standing_matrix());
mat4_rotateQuat(m, quat);
quat_getAngleAxis(quat_fromMat4(quat, m), angle, ax, ay, az);
}
static void webvrGetEyePose(HeadsetEye eye, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
int i = eye == EYE_LEFT ? 0 : 1;
emscripten_vr_get_eye_offset(i, x, y, z);
float m[16];
mat4_multiply(mat4_identity(m), emscripten_vr_get_sitting_to_standing_matrix());
mat4_multiply(m, mat4_invert(emscripten_vr_get_view_matrix(i)));
mat4_translate(m, *x, *y, *z);
*x = m[12];
*y = m[13];
*z = m[14];
float quat[4];
quat_getAngleAxis(quat_fromMat4(quat, m), angle, ax, ay, az);
}
static void webvrGetVelocity(float* x, float* y, float* z) {
float v[3];
emscripten_vr_get_velocity(&v[0], &v[1], &v[2]);
mat4_transformDirection(emscripten_vr_get_sitting_to_standing_matrix(), v);
*x = v[0];
*y = v[1];
*z = v[2];
}
static void webvrGetAngularVelocity(float* x, float* y, float* z) {
float v[3];
emscripten_vr_get_angular_velocity(&v[0], &v[1], &v[2]);
mat4_transformDirection(emscripten_vr_get_sitting_to_standing_matrix(), v);
*x = v[0];
*y = v[1];
*z = v[2];
}
static vec_controller_t* webvrGetControllers() {
int controllerCount = emscripten_vr_get_controller_count();
while (state.controllers.length > controllerCount) {
Controller* controller = vec_last(&state.controllers);
lovrRelease(controller);
vec_pop(&state.controllers);
}
while (state.controllers.length < controllerCount) {
Controller* controller = lovrAlloc(sizeof(Controller), free);
controller->id = state.controllers.length;
vec_push(&state.controllers, controller);
}
return &state.controllers;
}
static bool webvrControllerIsConnected(Controller* controller) {
return emscripten_vr_controller_is_present(controller->id);
}
static ControllerHand webvrControllerGetHand(Controller* controller) {
switch (emscripten_vr_controller_get_hand(controller->id)) {
case 0: return HAND_UNKNOWN;
case 1: return HAND_LEFT;
case 2: return HAND_RIGHT;
default: return HAND_UNKNOWN;
}
}
static void webvrControllerGetPose(Controller* controller, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
float v[3];
emscripten_vr_get_controller_position(controller->id, &v[0], &v[1], &v[2]);
mat4_transform(emscripten_vr_get_sitting_to_standing_matrix(), v);
*x = v[0];
*y = v[1];
*z = v[2];
float quat[4];
float m[16];
emscripten_vr_get_controller_orientation(controller->id, &quat[0], &quat[1], &quat[2], &quat[3]);
mat4_multiply(mat4_identity(m), emscripten_vr_get_sitting_to_standing_matrix());
mat4_rotateQuat(m, quat);
quat_getAngleAxis(quat_fromMat4(quat, m), angle, ax, ay, az);
}
static float webvrControllerGetAxis(Controller* controller, ControllerAxis axis) {
switch (axis) {
case CONTROLLER_AXIS_TRIGGER: return emscripten_vr_controller_get_axis(controller->id, -1);
case CONTROLLER_AXIS_TOUCHPAD_X: return emscripten_vr_controller_get_axis(controller->id, 0);
case CONTROLLER_AXIS_TOUCHPAD_Y: return emscripten_vr_controller_get_axis(controller->id, 1);
default: return 0;
}
}
static bool webvrControllerIsDown(Controller* controller, ControllerButton button) {
switch (button) {
case CONTROLLER_BUTTON_TOUCHPAD:
return emscripten_vr_controller_is_down(controller->id, 0);
case CONTROLLER_BUTTON_GRIP:
return emscripten_vr_controller_is_down(controller->id, 2);
case CONTROLLER_BUTTON_MENU:
return emscripten_vr_controller_is_down(controller->id, 3);
default: return 0;
}
}
static bool webvrControllerIsTouched(Controller* controller, ControllerButton button) {
return false;
}
static void webvrControllerVibrate(Controller* controller, float duration, float power) {
emscripten_vr_controller_vibrate(controller->id, duration * 1000, power);
}
static ModelData* webvrControllerNewModelData(Controller* controller) {
return NULL;
}
static void webvrRenderTo(void (*callback)(void*), void* userdata) {
state.renderCallback = callback;
emscripten_vr_set_render_callback(onRequestAnimationFrame, userdata);
}
static void webvrUpdate(float dt) {
}
HeadsetInterface lovrHeadsetWebVRDriver = {
DRIVER_WEBVR,
webvrInit,
webvrDestroy,
webvrGetType,
webvrDriverInit,
webvrDriverDestroy,
NULL, //HeadsetType (*getType)();
webvrGetOriginType,
webvrIsMounted,
NULL, //bool (*isMounted)();
webvrIsMirrored,
webvrSetMirrored,
webvrGetDisplayDimensions,
webvrGetClipDistance,
webvrSetClipDistance,
webvrGetBoundsWidth,
webvrGetBoundsDepth,
webvrGetPose,
webvrGetEyePose,
webvrGetVelocity,
webvrGetAngularVelocity,
webvrGetControllers,
webvrControllerIsConnected,
webvrControllerGetHand,
webvrControllerGetPose,
webvrControllerGetAxis,
webvrControllerIsDown,
webvrControllerIsTouched,
webvrControllerVibrate,
webvrControllerNewModelData,
webvrGetBoundsDimensions,
NULL, //void (*getPose)(float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az);
NULL, //void (*getEyePose)(HeadsetEye eye, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az);
NULL, //void (*getVelocity)(float* x, float* y, float* z);
NULL, //void (*getAngularVelocity)(float* x, float* y, float* z);
NULL, //vec_controller_t* (*getControllers)();
NULL, //bool (*controllerIsConnected)(Controller* controller);
NULL, //ControllerHand (*controllerGetHand)(Controller* controller);
NULL, //void (*controllerGetPose)(Controller* controller, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az);
NULL, //float (*controllerGetAxis)(Controller* controller, ControllerAxis axis);
NULL, //bool (*controllerIsDown)(Controller* controller, ControllerButton button);
NULL, //bool (*controllerIsTouched)(Controller* controller, ControllerButton button);
NULL, //void (*controllerVibrate)(Controller* controller, float duration, float power);
NULL, //ModelData* (*controllerNewModelData)(Controller* controller);
webvrRenderTo,
webvrUpdate
NULL //void (*update)(float dt);
};

136
src/resources/lovr.js Normal file
View File

@ -0,0 +1,136 @@
var LibraryLOVR = {
$lovr: {
WebVR: {
ORIGIN_HEAD: 0,
ORIGIN_FLOOR: 1,
initialized: false,
mirrored: true,
renderCallback: null,
renderUserdata: null,
frameData: null,
width: 0,
height: 0,
init: function() {
if (lovr.WebVR.initialized) {
return;
}
var display, canvas;
lovr.WebVR.initialized = true;
lovr.WebVR.canvas = canvas = Module['canvas'];
lovr.WebVR.width = canvas.width;
lovr.WebVR.height = canvas.height;
lovr.WebVR.frameData = new VRFrameData();
!function findDisplay() {
navigator.getVRDisplays && navigator.getVRDisplays().then(function(displays) {
lovr.WebVR.display = display = displays[0];
});
}();
!function onResize() {
if (display && display.isPresenting) {
var eyeParams = display.getEyeParameters('left');
lovr.WebVR.width = eyeParams.renderWidth;
lovr.WebVR.height = eyeParams.renderHeight;
canvas.width = lovr.WebVR.width * 2;
canvas.height = lovr.WebVR.height;
} else {
canvas.width = lovr.WebVR.width = canvas.parentElement.offsetWidth * window.devicePixelRatio;
canvas.height = lovr.WebVR.height = canvas.parentElement.offsetHeight * window.devicePixelRatio;
}
}();
window.requestAnimationFrame(function onAnimationFrame() {
if (display) {
display.requestAnimationFrame(onAnimationFrame);
display.getFrameData(lovr.WebVR.frameData);
if (display.isPresenting && lovr.WebVR.renderCallback) {
Runtime.dynCall('vi', lovr.WebVR.renderCallback, lovr.WebVR.renderUserdata);
display.submitFrame();
}
} else {
window.requestAnimationFrame(onAnimationFrame);
}
});
window.addEventListener('vrdisplayconnect', function(event) {
if (!display) {
lovr.WebVR.display = display = event.display;
}
});
window.addEventListener('vrdisplaydisconnect', function(event) {
if (!display || event.display === display) {
lovr.WebVR.display = display = null;
findDisplay();
}
});
window.addEventListener('lovr.entervr', function() {
if (display && !display.isPresenting) {
display.requestPresent([{ source: canvas }]);
}
});
window.addEventListener('lovr.exitvr', function() {
if (display && display.isPresenting) {
display.exitPresent();
}
});
window.addEventListener('vrdisplaypresentchange', onResize);
window.addEventListener('resize', onResize);
}
}
},
webvrInit: function() {
lovr.WebVR.init();
},
webvrGetOriginType: function() {
return WebVR.display && WebVR.display.stageParameters ? ORIGIN_FLOOR : ORIGIN_HEAD;
},
webvrIsMirrored: function() {
return lovr.WebVR.mirrored;
},
webvrSetMirrored: function(mirror) {
lovr.WebVR.mirrored = mirror;
},
webvrGetDisplayDimensions: function(width, height) {
Module.setValue(width, lovr.WebVR.width, 'i32');
Module.setValue(height, lovr.WebVR.height, 'i32');
},
webvrGetClipDistance: function(clipNear, clipFar) {
Module.setValue(clipNear, lovr.WebVR.display ? lovr.WebVR.display.depthNear : 0, 'float');
Module.setValue(clipFar, lovr.WebVR.display ? lovr.WebVR.display.depthFar : 0, 'float');
},
webvrSetClipDistance: function(clipNear, clipFar) {
if (lovr.WebVR.display) {
lovr.WebVR.display.depthNear = clipNear;
lovr.WebVR.display.depthFar = clipFar;
}
},
webvrGetBoundsDimensions: function(width, depth) {
Module.setValue(width, lovr.WebVR.display && lovr.WebVR.display.stageParameters ? lovr.WebVR.display.stageParameters.sizeX : 0);
Module.setValue(depth, lovr.WebVR.display && lovr.WebVR.display.stageParameters ? lovr.WebVR.display.stageParameters.sizeZ : 0);
},
webvrRenderTo: function(callback, userdata) {
lovr.WebVR.renderCallback = callback;
lovr.WebVR.renderUserdata = userdata;
}
};
autoAddDeps(LibraryLOVR, '$lovr');
mergeInto(LibraryManager.library, LibraryLOVR);