mirror of https://github.com/bjornbytes/lovr.git
Update webxr driver; rm webvr;
This commit is contained in:
parent
1b5e7b2203
commit
65470f1e0e
|
@ -20,7 +20,6 @@ option(LOVR_ENABLE_JSON "Bundle with lua-cjson" ON)
|
|||
option(LOVR_USE_LUAJIT "Use LuaJIT instead of Lua" ON)
|
||||
option(LOVR_USE_OPENVR "Enable the OpenVR backend for the headset module" ON)
|
||||
option(LOVR_USE_OPENXR "Enable the OpenXR backend for the headset module" OFF)
|
||||
option(LOVR_USE_WEBVR "Enable the WebVR backend for the headset module" OFF)
|
||||
option(LOVR_USE_WEBXR "Enable the WebXR 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_VRAPI "Enable the VrApi backend for the headset module" OFF)
|
||||
|
@ -50,16 +49,8 @@ if(EMSCRIPTEN)
|
|||
"-s FULL_ES2=1 "
|
||||
"-s FULL_ES3=1 "
|
||||
"-s FORCE_FILESYSTEM=1 "
|
||||
"-s \"EXPORTED_FUNCTIONS=[ "
|
||||
"'_main','_lovrDestroy',"
|
||||
"'_lovrCanvasCreateFromHandle',"
|
||||
"'_lovrGraphicsSetCamera',"
|
||||
"'_webvr_onAnimationFrame',"
|
||||
"'_mat4_set','_mat4_identity','_mat4_invert','_mat4_multiply','_mat4_rotateQuat','_mat4_transform','_mat4_transformDirection',"
|
||||
"'_quat_fromMat4','_quat_getAngleAxis'"
|
||||
"]\" "
|
||||
"-s \"EXPORTED_FUNCTIONS=['_main','_lovrDestroy','_lovrCanvasCreateFromHandle','_lovrCanvasDestroy','_lovrGraphicsSetCamera']\" "
|
||||
"-s \"EXTRA_EXPORTED_RUNTIME_METHODS=['getValue','setValue']\" "
|
||||
"--js-library \"${CMAKE_CURRENT_SOURCE_DIR}/src/resources/webvr.js\" "
|
||||
"--js-library \"${CMAKE_CURRENT_SOURCE_DIR}/src/resources/webxr.js\" "
|
||||
"--shell-file \"${CMAKE_CURRENT_SOURCE_DIR}/src/resources/lovr.html\""
|
||||
)
|
||||
|
@ -69,7 +60,6 @@ if(EMSCRIPTEN)
|
|||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}")
|
||||
set(CMAKE_EXECUTABLE_SUFFIX ".html")
|
||||
set(LOVR_USE_WEBVR ON)
|
||||
set(LOVR_USE_WEBXR ON)
|
||||
set(LOVR_USE_OPENVR OFF)
|
||||
elseif(ANDROID)
|
||||
|
@ -342,7 +332,6 @@ set(LOVR_SRC
|
|||
src/main.c
|
||||
src/core/arr.c
|
||||
src/core/fs.c
|
||||
src/core/maf.c
|
||||
src/core/map.c
|
||||
src/core/png.c
|
||||
src/core/ref.c
|
||||
|
@ -490,10 +479,6 @@ if(LOVR_ENABLE_HEADSET)
|
|||
add_definitions(-DLOVR_USE_PICO)
|
||||
target_sources(lovr PRIVATE src/modules/headset/headset_pico.c)
|
||||
endif()
|
||||
if(LOVR_USE_WEBVR)
|
||||
add_definitions(-DLOVR_USE_WEBVR)
|
||||
target_sources(lovr PRIVATE src/modules/headset/headset_webvr.c)
|
||||
endif()
|
||||
if(LOVR_USE_WEBXR)
|
||||
add_definitions(-DLOVR_USE_WEBXR)
|
||||
target_sources(lovr PRIVATE src/modules/headset/headset_webxr.c)
|
||||
|
|
|
@ -21,7 +21,7 @@ You can use LÖVR to easily create VR experiences without much setup or programm
|
|||
Features
|
||||
---
|
||||
|
||||
- **Cross-Platform** - Runs on Windows, Mac, Linux, Android, and on the web using WebAssembly and WebVR.
|
||||
- **Cross-Platform** - Runs on Windows, Mac, Linux, Android, and on the web using WebAssembly and WebXR.
|
||||
- **Cross-Device** - Supports Vive/Index, Oculus Rift/Go/Quest, Pico, Windows MR, and has a VR simulator.
|
||||
- **Beginner-friendly** - Simple VR scenes can be created in just a few lines of Lua.
|
||||
- **Fast** - Writen in C99 and scripted with LuaJIT, includes optimized single-pass stereo rendering.
|
||||
|
|
2
Tupfile
2
Tupfile
|
@ -6,7 +6,6 @@ SRC += src/main.c
|
|||
endif
|
||||
SRC += src/core/arr.c
|
||||
SRC += src/core/fs.c
|
||||
SRC += src/core/maf.c
|
||||
SRC += src/core/map.c
|
||||
ifneq (@(PICO),y)
|
||||
SRC += src/core/os_$(PLATFORM).c
|
||||
|
@ -30,7 +29,6 @@ SRC_@(HEADSET)@(OPENXR) += src/modules/headset/headset_openxr.c
|
|||
SRC_@(HEADSET)@(OCULUS) += src/modules/headset/headset_oculus.c
|
||||
SRC_@(HEADSET)@(VRAPI) += src/modules/headset/headset_vrapi.c
|
||||
SRC_@(HEADSET)@(PICO) += src/modules/headset/headset_pico.c
|
||||
SRC_@(HEADSET)@(WEBVR) += src/modules/headset/headset_webvr.c
|
||||
SRC_@(HEADSET)@(WEBXR) += src/modules/headset/headset_webxr.c
|
||||
SRC_@(HEADSET)@(LEAP) += src/modules/headset/headset_leap.c
|
||||
SRC_@(MATH) += src/modules/math/*.c
|
||||
|
|
|
@ -265,12 +265,7 @@ ifeq ($(PLATFORM),web)
|
|||
LDFLAGS += -s FULL_ES3
|
||||
LDFLAGS += -s GL_PREINITIALIZED_CONTEXT
|
||||
LDFLAGS += -s FORCE_FILESYSTEM
|
||||
LDFLAGS += -s EXPORTED_FUNCTIONS="[
|
||||
LDFLAGS += '_main',
|
||||
LDFLAGS += '_lovrCanvasCreateFromHandle','_lovrGraphicsSetCamera',
|
||||
LDFLAGS += '_mat4_set','_mat4_identity','_mat4_invert','_mat4_multiply','_mat4_rotateQuat',
|
||||
LDFLAGS += '_mat4_transform','_mat4_transformDirection'
|
||||
LDFLAGS += ]"
|
||||
LDFLAGS += -s EXPORTED_FUNCTIONS="['_main','_lovrCanvasCreateFromHandle','_lovrCanvasDestroy','_lovrGraphicsSetCamera']"
|
||||
LDFLAGS_@(WEBXR) += --js-library $(ROOT)/src/resources/webxr.js
|
||||
LDFLAGS += --shell-file $(ROOT)/src/resources/lovr.html
|
||||
CFLAGS_@(THREAD) += -s USE_PTHREADS=1
|
||||
|
|
|
@ -16,7 +16,6 @@ StringEntry HeadsetDrivers[] = {
|
|||
[DRIVER_OPENXR] = ENTRY("openxr"),
|
||||
[DRIVER_VRAPI] = ENTRY("vrapi"),
|
||||
[DRIVER_PICO] = ENTRY("pico"),
|
||||
[DRIVER_WEBVR] = ENTRY("webvr"),
|
||||
[DRIVER_WEBXR] = ENTRY("webxr"),
|
||||
{ 0 }
|
||||
};
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#define MAF_EXPORT
|
||||
#include "maf.h"
|
|
@ -53,7 +53,9 @@ void lovrEventDestroy() {
|
|||
for (size_t i = 0; i < state.events.length; i++) {
|
||||
Event* event = &state.events.data[i];
|
||||
switch (event->type) {
|
||||
#if LOVR_ENABLE_THREAD
|
||||
case EVENT_THREAD_ERROR: lovrRelease(Thread, event->data.thread.thread); break;
|
||||
#endif
|
||||
case EVENT_CUSTOM:
|
||||
for (uint32_t j = 0; j < event->data.custom.count; j++) {
|
||||
lovrVariantDestroy(&event->data.custom.data[j]);
|
||||
|
|
|
@ -36,9 +36,6 @@ bool lovrHeadsetInit(HeadsetDriver* drivers, size_t count, float offset, uint32_
|
|||
#ifdef LOVR_USE_PICO
|
||||
case DRIVER_PICO: interface = &lovrHeadsetPicoDriver; break;
|
||||
#endif
|
||||
#ifdef LOVR_USE_WEBVR
|
||||
case DRIVER_WEBVR: interface = &lovrHeadsetWebVRDriver; break;
|
||||
#endif
|
||||
#ifdef LOVR_USE_WEBXR
|
||||
case DRIVER_WEBXR: interface = &lovrHeadsetWebXRDriver; break;
|
||||
#endif
|
||||
|
|
|
@ -18,7 +18,6 @@ typedef enum {
|
|||
DRIVER_OPENXR,
|
||||
DRIVER_VRAPI,
|
||||
DRIVER_PICO,
|
||||
DRIVER_WEBVR,
|
||||
DRIVER_WEBXR
|
||||
} HeadsetDriver;
|
||||
|
||||
|
@ -150,7 +149,6 @@ extern HeadsetInterface lovrHeadsetOpenVRDriver;
|
|||
extern HeadsetInterface lovrHeadsetOpenXRDriver;
|
||||
extern HeadsetInterface lovrHeadsetVrApiDriver;
|
||||
extern HeadsetInterface lovrHeadsetPicoDriver;
|
||||
extern HeadsetInterface lovrHeadsetWebVRDriver;
|
||||
extern HeadsetInterface lovrHeadsetWebXRDriver;
|
||||
extern HeadsetInterface lovrHeadsetDesktopDriver;
|
||||
extern HeadsetInterface lovrHeadsetLeapMotionDriver;
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
#include "headset/headset.h"
|
||||
#include "graphics/graphics.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Provided by resources/webvr.js
|
||||
extern bool webvr_init(float offset, uint32_t msaa);
|
||||
extern void webvr_destroy(void);
|
||||
extern bool webvr_getName(char* name, size_t length);
|
||||
extern HeadsetOrigin webvr_getOriginType(void);
|
||||
extern double webvr_getDisplayTime(void);
|
||||
extern void webvr_getDisplayDimensions(uint32_t* width, uint32_t* height);
|
||||
extern const float* webvr_getDisplayMask(uint32_t* count);
|
||||
extern uint32_t webvr_getViewCount(void);
|
||||
extern bool webvr_getViewPose(uint32_t view, float* position, float* orientation);
|
||||
extern bool webvr_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down);
|
||||
extern void webvr_getClipDistance(float* near, float* far);
|
||||
extern void webvr_setClipDistance(float near, float far);
|
||||
extern void webvr_getBoundsDimensions(float* width, float* depth);
|
||||
extern const float* webvr_getBoundsGeometry(uint32_t* count);
|
||||
extern bool webvr_getPose(Device device, float* position, float* orientation);
|
||||
extern bool webvr_getVelocity(Device device, float* velocity, float* angularVelocity);
|
||||
extern bool webvr_isDown(Device device, DeviceButton button, bool* down, bool* changed);
|
||||
extern bool webvr_isTouched(Device device, DeviceButton button, bool* touched);
|
||||
extern bool webvr_getAxis(Device device, DeviceAxis axis, float* value);
|
||||
extern bool webvr_vibrate(Device device, float strength, float duration, float frequency);
|
||||
extern struct ModelData* webvr_newModelData(Device device, bool animated);
|
||||
extern bool webvr_animate(Device device, struct Model* model);
|
||||
extern void webvr_update(float dt);
|
||||
|
||||
static struct {
|
||||
void (*renderCallback)(void*);
|
||||
void* renderData;
|
||||
} state;
|
||||
|
||||
void webvr_onAnimationFrame(float* leftView, float* rightView, float* leftProjection, float* rightProjection) {
|
||||
Camera camera = { .canvas = NULL, .stereo = true };
|
||||
memcpy(camera.projection[0], leftProjection, 16 * sizeof(float));
|
||||
memcpy(camera.projection[1], rightProjection, 16 * sizeof(float));
|
||||
memcpy(camera.viewMatrix[0], leftView, 16 * sizeof(float));
|
||||
memcpy(camera.viewMatrix[1], rightView, 16 * sizeof(float));
|
||||
lovrGraphicsSetCamera(&camera, true);
|
||||
state.renderCallback(state.renderData);
|
||||
lovrGraphicsSetCamera(NULL, false);
|
||||
}
|
||||
|
||||
void webvr_renderTo(void (*callback)(void*), void* userdata) {
|
||||
state.renderCallback = callback;
|
||||
state.renderData = userdata;
|
||||
}
|
||||
|
||||
HeadsetInterface lovrHeadsetWebVRDriver = {
|
||||
.driverType = DRIVER_WEBVR,
|
||||
.init = webvr_init,
|
||||
.destroy = webvr_destroy,
|
||||
.getName = webvr_getName,
|
||||
.getOriginType = webvr_getOriginType,
|
||||
.getDisplayTime = webvr_getDisplayTime,
|
||||
.getDisplayDimensions = webvr_getDisplayDimensions,
|
||||
.getDisplayMask = webvr_getDisplayMask,
|
||||
.getViewCount = webvr_getViewCount,
|
||||
.getViewPose = webvr_getViewPose,
|
||||
.getViewAngles = webvr_getViewAngles,
|
||||
.getClipDistance = webvr_getClipDistance,
|
||||
.setClipDistance = webvr_setClipDistance,
|
||||
.getBoundsDimensions = webvr_getBoundsDimensions,
|
||||
.getBoundsGeometry = webvr_getBoundsGeometry,
|
||||
.getPose = webvr_getPose,
|
||||
.getVelocity = webvr_getVelocity,
|
||||
.isDown = webvr_isDown,
|
||||
.isTouched = webvr_isTouched,
|
||||
.getAxis = webvr_getAxis,
|
||||
.vibrate = webvr_vibrate,
|
||||
.newModelData = webvr_newModelData,
|
||||
.animate = webvr_animate,
|
||||
.renderTo = webvr_renderTo,
|
||||
.update = webvr_update
|
||||
};
|
|
@ -27,7 +27,7 @@ extern void webxr_renderTo(void (*callback)(void*), void* userdata);
|
|||
extern void webxr_update(float dt);
|
||||
|
||||
HeadsetInterface lovrHeadsetWebXRDriver = {
|
||||
.driverType = DRIVER_WEBVR,
|
||||
.driverType = DRIVER_WEBXR,
|
||||
.init = webxr_init,
|
||||
.destroy = webxr_destroy,
|
||||
.getName = webxr_getName,
|
||||
|
|
|
@ -99,7 +99,7 @@ function lovr.boot()
|
|||
debug = false
|
||||
},
|
||||
headset = {
|
||||
drivers = { 'leap', 'openxr', 'oculus', 'vrapi', 'pico', 'openvr', 'webxr', 'webvr', 'desktop' },
|
||||
drivers = { 'leap', 'openxr', 'oculus', 'vrapi', 'pico', 'openvr', 'webxr', 'desktop' },
|
||||
offset = 1.7,
|
||||
msaa = 4
|
||||
},
|
||||
|
|
|
@ -56,8 +56,8 @@
|
|||
});
|
||||
|
||||
var Module = window.Module = {
|
||||
arguments: ['./'],
|
||||
preRun: [findDisplay],
|
||||
arguments: [],
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
print: console.log.bind(console),
|
||||
printErr: console.error.bind(console),
|
||||
|
@ -66,29 +66,51 @@
|
|||
preinitializedWebGLContext: context
|
||||
};
|
||||
|
||||
function findDisplay() {
|
||||
if (navigator.getVRDisplays) {
|
||||
Module.addRunDependency('lovrDisplay');
|
||||
navigator.getVRDisplays().
|
||||
then(function(displays) {
|
||||
Module.lovrDisplay = displays[0];
|
||||
container.appendChild(button);
|
||||
}).finally(function() {
|
||||
Module.removeRunDependency('lovrDisplay');
|
||||
});
|
||||
}
|
||||
// To run a LÖVR project on this page, create a .lovr (zip) file of its folder and serve it
|
||||
// alongside the HTML file. Then set the 'project' variable below to the project's filename.
|
||||
// This downloads the .lovr file into the virtual filesystem and adds it as a virtual command
|
||||
// line argument before booting up LÖVR.
|
||||
// Example: var project = 'app.lovr';
|
||||
var project = null;
|
||||
if (project) {
|
||||
Module.arguments.push(project);
|
||||
Module.preRun.push(function() {
|
||||
Module.FS_createPreloadedFile('/', project, project, true, false);
|
||||
});
|
||||
}
|
||||
|
||||
button.addEventListener('click', function() {
|
||||
if (Module.lovrDisplay && Module.lovrDisplay.capabilities.canPresent) {
|
||||
var eventName = Module.lovrDisplay.isPresenting ? 'lovr.exitvr' : 'lovr.entervr';
|
||||
window.dispatchEvent(new CustomEvent(eventName));
|
||||
}
|
||||
});
|
||||
// If WebXR is supported and immersive sessions are supported, add a button to the DOM that
|
||||
// controls starting/stopping the immersive session.
|
||||
if (navigator.xr) {
|
||||
navigator.xr.isSessionSupported('immersive-vr').then(function(supported) {
|
||||
if (!supported) return;
|
||||
|
||||
window.addEventListener('vrdisplaypresentchange', function() {
|
||||
button.textContent = Module.lovrDisplay.isPresenting ? 'Exit VR' : 'Enter VR';
|
||||
});
|
||||
container.appendChild(button);
|
||||
|
||||
var active = false;
|
||||
|
||||
function onEnter() {
|
||||
active = true;
|
||||
button.textContent = 'Exit VR';
|
||||
}
|
||||
|
||||
function onExit() {
|
||||
active = false;
|
||||
button.textContent = 'Enter VR';
|
||||
}
|
||||
|
||||
button.addEventListener('click', function() {
|
||||
if (!active) {
|
||||
Module.lovr.enterVR().then(function(session) {
|
||||
session.addEventListener('end', onExit);
|
||||
onEnter();
|
||||
});
|
||||
} else {
|
||||
Module.lovr.exitVR().then(onExit);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{{{ SCRIPT }}}
|
||||
|
|
|
@ -1,336 +0,0 @@
|
|||
var LibraryLOVR = {
|
||||
$webvr: {
|
||||
buttonMap: {
|
||||
'OpenVR Gamepad': [1, 1, null, 0, 2],
|
||||
'Oculus Touch (Left)': [1, 1, 0, null, 2, null, null, null, 3, 4],
|
||||
'Oculus Touch (Right)': [1, 1, 0, null, 2, null, 3, 4, null, null],
|
||||
'Spatial Controller (Spatial Interaction Source) 045E-065D': [0, 0, 1, 3, 2, 4]
|
||||
},
|
||||
|
||||
refreshGamepads: function(event) {
|
||||
if (event.gamepad.hand) {
|
||||
var device = ({
|
||||
'left': C.DEVICE_HAND_LEFT,
|
||||
'right': C.DEVICE_HAND_RIGHT
|
||||
})[event.gamepad.hand];
|
||||
|
||||
if (device) {
|
||||
webvr.gamepads[device] = event.gamepad;
|
||||
webvr.poses[device] = event.gamepad.pose;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
webvr_init: function(offset, msaa) {
|
||||
if (webvr.initialized || !Module.lovrDisplay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var a, b, c, d, e, canvas, display;
|
||||
webvr.initialized = true;
|
||||
webvr.display = display = Module.lovrDisplay;
|
||||
webvr.display.depthNear = .1;
|
||||
webvr.display.depthFar = 100;
|
||||
webvr.canvas = canvas = Module.canvas;
|
||||
webvr.frameData = new VRFrameData();
|
||||
webvr.gamepads = [];
|
||||
webvr.poses = [];
|
||||
webvr.offset = offset;
|
||||
webvr.poseTransform = Module._malloc(64);
|
||||
webvr.matA = a = Module._malloc(64);
|
||||
webvr.matB = b = Module._malloc(64);
|
||||
webvr.matC = c = Module._malloc(64);
|
||||
webvr.matD = d = Module._malloc(64);
|
||||
webvr.matE = e = Module._malloc(64);
|
||||
webvr.width = display.getEyeParameters('left').renderWidth * 2;
|
||||
webvr.height = display.getEyeParameters('left').renderHeight;
|
||||
Browser.setCanvasSize(webvr.width, webvr.height);
|
||||
|
||||
webvr.onentervr = function() {
|
||||
if (!display.isPresenting) {
|
||||
display.requestPresent([{ source: canvas }]);
|
||||
}
|
||||
};
|
||||
|
||||
webvr.onexitvr = function() {
|
||||
if (display.isPresenting) {
|
||||
display.exitPresent();
|
||||
}
|
||||
};
|
||||
|
||||
webvr.frameId = display.requestAnimationFrame(function onAnimationFrame() {
|
||||
webvr.frameId = display.requestAnimationFrame(onAnimationFrame);
|
||||
display.getFrameData(webvr.frameData);
|
||||
webvr.poses[0] = webvr.frameData.pose;
|
||||
|
||||
if (webvr.display.stageParameters && webvr.display.stageParameters.sittingToStandingTransform) {
|
||||
HEAPF32.set(webvr.display.stageParameters.sittingToStandingTransform, webvr.poseTransform >> 2);
|
||||
} else {
|
||||
Module._mat4_identity(webvr.poseTransform);
|
||||
HEAPF32[webvr.poseTransform >> 2 + 13] = webvr.offset;
|
||||
}
|
||||
|
||||
Module._mat4_set(e, webvr.poseTransform);
|
||||
Module._mat4_invert(e);
|
||||
HEAPF32.set(webvr.frameData.leftViewMatrix, a >> 2);
|
||||
HEAPF32.set(webvr.frameData.rightViewMatrix, b >> 2);
|
||||
HEAPF32.set(webvr.frameData.leftProjectionMatrix, c >> 2);
|
||||
HEAPF32.set(webvr.frameData.rightProjectionMatrix, d >> 2);
|
||||
Module._mat4_multiply(a, e);
|
||||
Module._mat4_multiply(b, e);
|
||||
Module._webvr_onAnimationFrame(a, b, c, d);
|
||||
|
||||
if (display.isPresenting) {
|
||||
display.submitFrame();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('lovr.entervr', webvr.onentervr);
|
||||
window.addEventListener('lovr.exitvr', webvr.onexitvr);
|
||||
window.addEventListener('vrdisplaypresentchange', webvr.onvrdisplaypresentchange);
|
||||
window.addEventListener('gamepadconnected', webvr.refreshGamepads);
|
||||
window.addEventListener('gamepaddisconnected', webvr.refreshGamepads);
|
||||
return true;
|
||||
},
|
||||
|
||||
webvr_destroy: function() {
|
||||
if (!webvr.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
webvr.initialized = false;
|
||||
Module._free(webvr.poseTransform);
|
||||
Module._free(webvr.matA);
|
||||
Module._free(webvr.matB);
|
||||
Module._free(webvr.matC);
|
||||
Module._free(webvr.matD);
|
||||
Module._free(webvr.matE);
|
||||
|
||||
window.removeEventListener('lovr.entervr', webvr.onentervr);
|
||||
window.removeEventListener('lovr.exitvr', webvr.onexitvr);
|
||||
window.removeEventListener('vrdisplaypresentchange', webvr.onvrdisplaypresentchange);
|
||||
window.removeEventListener('gamepadconnected', webvr.refreshGamepads);
|
||||
window.removeEventListener('gamepaddisconnected', webvr.refreshGamepads);
|
||||
|
||||
if (webvr.frameId) {
|
||||
webvr.display.cancelAnimationFrame(webvr.frameId);
|
||||
}
|
||||
},
|
||||
|
||||
webvr_getName: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
webvr_getOriginType: function() {
|
||||
return webvr.display.stageParameters ? C.ORIGIN_FLOOR : C.ORIGIN_HEAD;
|
||||
},
|
||||
|
||||
webvr_getDisplayTime: function() {
|
||||
return webvr.frameData.timestamp / 1000;
|
||||
},
|
||||
|
||||
webvr_getDisplayDimensions: function(width, height) {
|
||||
HEAPU32[width >> 2] = webvr.width;
|
||||
HEAPU32[height >> 2] = webvr.height;
|
||||
},
|
||||
|
||||
webvr_getDisplayMask: function(count) {
|
||||
HEAPU32[count >> 2] = 0;
|
||||
return 0;
|
||||
},
|
||||
|
||||
webvr_getViewCount: function() {
|
||||
return 2;
|
||||
},
|
||||
|
||||
webvr_getViewPose: function(view, position, orientation) {
|
||||
return false; // TODO
|
||||
},
|
||||
|
||||
webvr_getViewAngles: function(view, left, right, up, down) {
|
||||
return false; // TODO
|
||||
},
|
||||
|
||||
webvr_getClipDistance: function(clipNear, clipFar) {
|
||||
HEAPF32[clipNear >> 2] = webvr.display.depthNear;
|
||||
HEAPF32[clipFar >> 2] = webvr.display.depthFar;
|
||||
},
|
||||
|
||||
webvr_setClipDistance: function(clipNear, clipFar) {
|
||||
webvr.display.depthNear = clipNear;
|
||||
webvr.display.depthFar = clipFar;
|
||||
},
|
||||
|
||||
webvr_getBoundsDimensions: function(width, depth) {
|
||||
var stage = webvr.display.stageParameters;
|
||||
if (stage) {
|
||||
HEAPF32[width >> 2] = stage.sizeX;
|
||||
HEAPF32[depth >> 2] = stage.sizeZ;
|
||||
} else {
|
||||
HEAPF32[width >> 2] = HEAPF32[depth >> 2] = 0;
|
||||
}
|
||||
},
|
||||
|
||||
webvr_getBoundsGeometry: function(count) {
|
||||
HEAP32[count >> 2] = 0;
|
||||
return 0;
|
||||
},
|
||||
|
||||
webvr_getPose: function(device, position, orientation) {
|
||||
var pose = webvr.poses[device];
|
||||
if (!pose) { return false; }
|
||||
|
||||
if (pose.position) {
|
||||
HEAPF32.set(pose.position, position >> 2);
|
||||
Module._mat4_transform(webvr.poseTransform, position);
|
||||
} else {
|
||||
HEAPF32.fill(0, position >> 2, position >> 2 + 3);
|
||||
}
|
||||
|
||||
if (pose.orientation) {
|
||||
HEAPF32.set(pose.orientation, orientation >> 2);
|
||||
Module._mat4_set(webvr.matA, webvr.poseTransform);
|
||||
Module._mat4_rotateQuat(webvr.matA, orientation);
|
||||
Module._quat_fromMat4(orientation, webvr.matA);
|
||||
} else {
|
||||
HEAPF32.fill(0, orientation >> 2, orientation >> 2 + 4);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
webvr_getVelocity: function(device, velocity, angularVelocity) {
|
||||
var pose = webvr.poses[device];
|
||||
if (!pose) { return false; }
|
||||
|
||||
if (pose.linearVelocity) {
|
||||
HEAPF32.set(pose.linearVelocity, velocity >> 2);
|
||||
Module._mat4_transformDirection(webvr.poseTransform, velocity);
|
||||
} else {
|
||||
HEAPF32.fill(0, velocity >> 2, velocity >> 2 + 3);
|
||||
}
|
||||
|
||||
if (pose.angularVelocity) {
|
||||
HEAPF32.set(pose.angularVelocity, angularVelocity >> 2);
|
||||
Module._mat4_transformDirection(webvr.poseTransform, angularVelocity);
|
||||
} else {
|
||||
HEAPF32.fill(0, angularVelocity >> 2, angularVelocity >> 2 + 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
webvr_isDown: function(device, button, down, changed) {
|
||||
var gamepad = webvr.gamepads[device];
|
||||
|
||||
if (!gamepad || !gamepad.id || !webvr.buttonMap[gamepad.id] || !webvr.buttonMap[gamepad.id][button]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HEAPF32[down >> 2] = gamepad.buttons[webvr.buttonMap[gamepad.id][button]].pressed;
|
||||
HEAPF32[changed >> 2] = false; // TODO
|
||||
return true;
|
||||
},
|
||||
|
||||
webvr_isTouched: function(device, button, touched) {
|
||||
var gamepad = webvr.gamepads[device];
|
||||
|
||||
if (!gamepad || !gamepad.id || !webvr.buttonMap[gamepad.id] || !webvr.buttonMap[gamepad.id][button]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HEAPF32[touched >> 2] = gamepad.buttons[webvr.buttonMap[gamepad.id][button]].touched;
|
||||
return true;
|
||||
},
|
||||
|
||||
webvr_getAxis: function(device, axis, value) {
|
||||
var gamepad = webvr.gamepads[device];
|
||||
|
||||
if (!gamepad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gamepad.id.startsWith('OpenVR')) {
|
||||
switch (axis) {
|
||||
case C.AXIS_TRIGGER: HEAPF32[value >> 2] = gamepad.buttons[1].value; return true;
|
||||
case C.AXIS_TOUCHPAD:
|
||||
HEAPF32[value >> 2 + 0] = gamepad.axes[0];
|
||||
HEAPF32[value >> 2 + 1] = gamepad.axes[1];
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
} else if (gamepad.id.startsWith('Oculus')) {
|
||||
switch (axis) {
|
||||
case C.AXIS_TRIGGER: HEAPF32[value >> 2] = gamepad.buttons[1].value; return true;
|
||||
case C.AXIS_GRIP: HEAPF32[value >> 2] = gamepad.buttons[2].value; return true;
|
||||
case C.AXIS_THUMBSTICK:
|
||||
HEAPF32[value >> 2 + 0] = gamepad.axes[0];
|
||||
HEAPF32[value >> 2 + 1] = gamepad.axes[1];
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
} else if (gamepad.id.startsWith('Spatial Controller')) {
|
||||
switch (axis) {
|
||||
case C.AXIS_TRIGGER: HEAPF32[value >> 2] = gamepad.buttons[0].value; return true;
|
||||
case C.AXIS_THUMBSTICK:
|
||||
HEAPF32[value >> 2 + 0] = gamepad.axes[0];
|
||||
HEAPF32[value >> 2 + 1] = gamepad.axes[1];
|
||||
return true;
|
||||
case C.AXIS_TOUCHPAD:
|
||||
HEAPF32[value >> 2 + 0] = gamepad.axes[2];
|
||||
HEAPF32[value >> 2 + 1] = gamepad.axes[3];
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
webvr_vibrate: function(device, strength, duration, frequency) {
|
||||
var gamepad = webvr.gamepads[device];
|
||||
|
||||
if (gamepad && gamepad.hapticActuators && gamepad.hapticActuators[0]) {
|
||||
gamepad.hapticActuators[0].pulse(strength, duration * 1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
webvr_newModelData: function(device, animated) {
|
||||
return C.NULL;
|
||||
},
|
||||
|
||||
webvr_animate: function(device, model) {
|
||||
return false;
|
||||
},
|
||||
|
||||
webvr_update: function(dt) {
|
||||
//
|
||||
},
|
||||
|
||||
$C: {
|
||||
NULL: 0,
|
||||
|
||||
// HeadsetOrigin
|
||||
ORIGIN_HEAD: 0,
|
||||
ORIGIN_FLOOR: 1,
|
||||
|
||||
// Device
|
||||
DEVICE_HAND_LEFT: 0,
|
||||
DEVICE_HAND_RIGHT: 1,
|
||||
|
||||
// DeviceAxis
|
||||
AXIS_TRIGGER: 0,
|
||||
AXIS_THUMBSTICK: 1,
|
||||
AXIS_TOUCHPAD: 2,
|
||||
AXIS_PINCH: 3,
|
||||
AXIS_GRIP: 4
|
||||
}
|
||||
};
|
||||
|
||||
autoAddDeps(LibraryLOVR, '$webvr');
|
||||
autoAddDeps(LibraryLOVR, '$C');
|
||||
mergeInto(LibraryManager.library, LibraryLOVR);
|
|
@ -6,92 +6,187 @@ var webxr = {
|
|||
return false;
|
||||
}
|
||||
|
||||
state.layer = null;
|
||||
state.sessions = {};
|
||||
state.session = null;
|
||||
state.frame = null;
|
||||
state.canvas = null;
|
||||
state.camera = null;
|
||||
state.clipNear = .1;
|
||||
state.clipFar = 1000.0;
|
||||
state.referenceSpaceType = null;
|
||||
state.renderCallback = null;
|
||||
state.renderUserdata = null;
|
||||
state.animationFrame = null;
|
||||
state.displayTime = null;
|
||||
state.camera = Module._malloc(264 /* sizeof(Camera) */);
|
||||
state.boundsGeometry = 0; /* NULL */
|
||||
state.boundsGeometryCount = 0;
|
||||
|
||||
navigator.xr.requestSession('inline').then(function(session) {
|
||||
state.referenceSpaceType = 'viewer';
|
||||
session.requestReferenceSpace(state.referenceSpaceType).then(function(referenceSpace) {
|
||||
state.session = session;
|
||||
state.layer = new XRWebGLLayer(session, Module.preinitializedWebGLContext);
|
||||
var mappings = {
|
||||
'oculus-touch-left': [0, 3, null, 1, null, null, null, 4, 5],
|
||||
'oculus-touch-right': [0, 3, null, 1, null, 4, 5, null, null],
|
||||
'valve-index': [0, 3, 2, 1, null, 4, null, 4, null],
|
||||
'microsoft-mixed-reality': [0, 3, 2, 1],
|
||||
'htc-vive': [0, null, 2, 1],
|
||||
'generic-trigger': [0],
|
||||
'generic-trigger-touchpad': [0, null, 2],
|
||||
'generic-trigger-thumbstick': [0, 3],
|
||||
'generic-trigger-touchpad-thumbstick': [0, 3, 2],
|
||||
'generic-trigger-squeeze': [0, null, null, 1],
|
||||
'generic-trigger-squeeze-touchpad': [0, null, 2, 1],
|
||||
'generic-trigger-squeeze-touchpad-thumbstick': [0, 3, 2, 1],
|
||||
'generic-trigger-squeeze-thumbstick': [0, 3, null, 1],
|
||||
'generic-hand-select': [0],
|
||||
};
|
||||
|
||||
var framebuffer = 0;
|
||||
function startSession(mode, options) {
|
||||
return navigator.xr.requestSession(mode, options).then(function(session) {
|
||||
var spaces = {
|
||||
'inline': ['viewer'],
|
||||
'immersive-vr': ['bounded-floor', 'local-floor']
|
||||
};
|
||||
|
||||
if (state.layer.framebuffer) {
|
||||
framebuffer = GL.getNewId(GL.framebuffers);
|
||||
GL.framebuffers[framebuffer] = state.layer.framebuffer;
|
||||
}
|
||||
// This is confusing but it basically keeps trying to request successive reference spaces
|
||||
// until one succeeds. $space is a promise that resolves to a reference space
|
||||
var $space = spaces[mode].reduce(function(chain, spaceType) {
|
||||
return chain.catch(function() {
|
||||
session.spaceType = spaceType;
|
||||
return session.requestReferenceSpace(spaceType);
|
||||
});
|
||||
}, Promise.reject());
|
||||
|
||||
var sizeof_CanvasFlags = 16;
|
||||
var flags = Module.stackAlloc(sizeof_CanvasFlags);
|
||||
HEAPU8.fill(0, flags, flags + sizeof_CanvasFlags); // memset(&flags, 0, sizeof(CanvasFlags));
|
||||
var width = state.layer.framebufferWidth;
|
||||
var height = state.layer.framebufferHeight;
|
||||
state.canvas = Module['_lovrCanvasCreateFromHandle'](width, height, flags, framebuffer, 0, 0, 1, true);
|
||||
Module.stackRestore(flags);
|
||||
session.inputSources = [];
|
||||
|
||||
var sizeof_Camera = 264;
|
||||
state.camera = Module._malloc(sizeof_Camera);
|
||||
HEAPU32[(state.camera + 4) >> 2] = state.canvas; // state.camera.canvas = state.canvas
|
||||
return $space.then(function(space) {
|
||||
state.session = session;
|
||||
state.sessions[mode] = session;
|
||||
session.layer = new XRWebGLLayer(session, Module.preinitializedWebGLContext);
|
||||
session.updateRenderState({
|
||||
baseLayer: session.layer,
|
||||
inlineVerticalFieldOfView: mode === 'inline' ? (67.0 * Math.PI / 180.0) : undefined
|
||||
});
|
||||
|
||||
session.updateRenderState({
|
||||
baseLayer: state.layer
|
||||
});
|
||||
|
||||
state.animationFrame = session.requestAnimationFrame(function onFrame(t, frame) {
|
||||
state.animationFrame = session.requestAnimationFrame(onFrame);
|
||||
state.displayTime = t;
|
||||
state.frame = frame;
|
||||
|
||||
var views = frame.getViewerPose(referenceSpace).views;
|
||||
|
||||
var stereo = views.length > 1;
|
||||
HEAPU8[state.camera + 0] = stereo; // camera.stereo = stereo
|
||||
|
||||
var matrices = (state.camera + 8) >> 2;
|
||||
HEAPF32.set(views[0].transform.inverse.matrix, matrices + 0);
|
||||
HEAPF32.set(views[0].projectionMatrix, matrices + 32);
|
||||
|
||||
if (stereo) {
|
||||
HEAPF32.set(views[1].transform.inverse.matrix, matrices + 16);
|
||||
HEAPF32.set(views[1].projectionMatrix, matrices + 48);
|
||||
if (session.spaceType.includes('floor')) {
|
||||
session.space = space;
|
||||
} else {
|
||||
session.space = space.getOffsetReferenceSpace(new XRRigidTransform({ y: -offset }));
|
||||
}
|
||||
|
||||
Module['_lovrGraphicsSetCamera'](state.camera, true);
|
||||
session.framebufferId = 0;
|
||||
|
||||
if (state.renderCallback) {
|
||||
if (session.layer.framebuffer) {
|
||||
session.framebufferId = GL.getNewId(GL.framebuffers);
|
||||
GL.framebuffers[session.framebufferId] = session.layer.framebuffer;
|
||||
}
|
||||
|
||||
var sizeof_CanvasFlags = 16;
|
||||
var flags = Module.stackAlloc(sizeof_CanvasFlags);
|
||||
HEAPU8.fill(0, flags, flags + sizeof_CanvasFlags); // memset(&flags, 0, sizeof(CanvasFlags));
|
||||
HEAPU8[flags + 12] = mode === 'inline' ? 0 : 1; // flags.stereo
|
||||
var width = session.layer.framebufferWidth;
|
||||
var height = session.layer.framebufferHeight;
|
||||
session.canvas = Module['_lovrCanvasCreateFromHandle'](width, height, flags, session.framebufferId, 0, 0, 1, true);
|
||||
Module.stackRestore(flags);
|
||||
|
||||
session.animationFrame = session.requestAnimationFrame(function onFrame(t, frame) {
|
||||
session.animationFrame = session.requestAnimationFrame(onFrame);
|
||||
session.displayTime = t;
|
||||
session.frame = frame;
|
||||
session.viewer = frame.getViewerPose(session.space);
|
||||
|
||||
if (!state.renderCallback) return;
|
||||
|
||||
var views = session.viewer.views;
|
||||
var stereo = views.length > 1;
|
||||
var matrices = (state.camera + 8) >> 2;
|
||||
HEAPU8[state.camera + 0] = stereo; // camera.stereo = stereo
|
||||
HEAPU32[(state.camera + 4) >> 2] = session.canvas; // camera.canvas = session.canvas
|
||||
HEAPF32.set(views[0].transform.inverse.matrix, matrices + 0);
|
||||
HEAPF32.set(views[0].projectionMatrix, matrices + 32);
|
||||
if (stereo) {
|
||||
HEAPF32.set(views[1].transform.inverse.matrix, matrices + 16);
|
||||
HEAPF32.set(views[1].projectionMatrix, matrices + 48);
|
||||
}
|
||||
|
||||
Module['_lovrGraphicsSetCamera'](state.camera, true);
|
||||
Module['dynCall_vi'](state.renderCallback, state.renderUserdata);
|
||||
}
|
||||
Module['_lovrGraphicsSetCamera'](0, false);
|
||||
});
|
||||
|
||||
Module['_lovrGraphicsSetCamera'](0, false);
|
||||
session.addEventListener('inputsourceschange', function(event) {
|
||||
session.inputSources.forEach(function(inputSource, i) {
|
||||
if (event.removed.includes(inputSource)) {
|
||||
session.inputSources[i] = null;
|
||||
}
|
||||
});
|
||||
|
||||
event.added.forEach(function(inputSource) {
|
||||
if (inputSource.handedness === 'left') {
|
||||
session.inputSources[1 /* DEVICE_HAND_LEFT */] = inputSource;
|
||||
} else if (inputSource.handedness === 'right') {
|
||||
session.inputSources[2 /* DEVICE_HAND_RIGHT */] = inputSource;
|
||||
}
|
||||
|
||||
for (var i = 0; i < inputSource.profiles.length; i++) {
|
||||
var profile = inputSource.profiles[i];
|
||||
|
||||
// So far Oculus touch controllers are the only "meaningfully handed" controllers
|
||||
// If more appear then a more general approach should be used
|
||||
if (profile === 'oculus-touch') {
|
||||
profile = profile + '-' + inputSource.handedness;
|
||||
}
|
||||
|
||||
if (mappings[profile]) {
|
||||
inputSource.mapping = mappings[profile];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
session.addEventListener('end', function() {
|
||||
delete state.sessions[session.mode];
|
||||
|
||||
if (session.canvas) {
|
||||
Module['_lovrCanvasDestroy'](session.canvas);
|
||||
Module._free(session.canvas - 4);
|
||||
}
|
||||
|
||||
if (session.framebufferId) {
|
||||
GL.framebuffers[session.framebufferId].name = 0;
|
||||
GL.framebuffers[session.framebufferId] = null;
|
||||
}
|
||||
|
||||
// If the immersive session ends (for any reason), switch back to the inline session
|
||||
if (session.mode === 'immersive-vr') {
|
||||
state.session = state.sessions.inline;
|
||||
}
|
||||
});
|
||||
|
||||
return session;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Module.lovr = Module.lovr || {};
|
||||
Module.lovr.enterVR = function() {
|
||||
return startSession('immersive-vr', {
|
||||
requiredFeatures: ['local-floor'],
|
||||
optionalFeatures: ['bounded-floor']
|
||||
});
|
||||
};
|
||||
|
||||
Module.lovr.exitVR = function() {
|
||||
return (state.session && state.session.mode === 'immersive-vr') ? state.session.end() : Promise.resolve();
|
||||
};
|
||||
|
||||
startSession('inline');
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
webxr_destroy: function() {
|
||||
function cleanup() {
|
||||
// TODO release canvas
|
||||
Module._free(state.camera|0);
|
||||
for (mode in state.sessions) {
|
||||
state.sessions[mode].end();
|
||||
}
|
||||
|
||||
if (state.session) {
|
||||
state.session.cancelAnimationFrame(state.animationFrame);
|
||||
state.session.end().then(cleanup);
|
||||
} else {
|
||||
cleanup();
|
||||
}
|
||||
Module._free(state.camera|0);
|
||||
Module._free(state.boundsGeometry|0);
|
||||
},
|
||||
|
||||
webxr_getName: function(name, size) {
|
||||
|
@ -99,20 +194,22 @@ var webxr = {
|
|||
},
|
||||
|
||||
webxr_getOriginType: function() {
|
||||
if (state.referenceSpaceType === 'local-floor' || state.referenceSpaceType === 'bounded-floor') {
|
||||
return 1; /* ORIGIN_FLOOR */
|
||||
}
|
||||
|
||||
return 0; /* ORIGIN_HEAD */
|
||||
if (!state.session) return 0;
|
||||
return state.session.spaceType.includes('floor') ? 1 /* ORIGIN_FLOOR */ : 0 /* ORIGIN_HEAD */;
|
||||
},
|
||||
|
||||
webxr_getDisplayTime: function() {
|
||||
return state.displayTime;
|
||||
return state.session ? (state.session.displayTime / 1000.0) : 0;
|
||||
},
|
||||
|
||||
webxr_getDisplayDimensions: function(width, height) {
|
||||
HEAPU32[width >> 2] = state.layer.framebufferWidth;
|
||||
HEAPU32[height >> 2] = state.layer.framebufferHeight;
|
||||
if (state.session) {
|
||||
HEAPU32[width >> 2] = state.session.layer.framebufferWidth;
|
||||
HEAPU32[height >> 2] = state.session.layer.framebufferHeight;
|
||||
} else {
|
||||
HEAPU32[width >> 2] = 0;
|
||||
HEAPU32[height >> 2] = 0;
|
||||
}
|
||||
},
|
||||
|
||||
webxr_getDisplayFrequency: function() {
|
||||
|
@ -124,34 +221,37 @@ var webxr = {
|
|||
},
|
||||
|
||||
webxr_getViewCount: function() {
|
||||
if (!state.frame) {
|
||||
if (!state.session) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return getViewerPose(state.frame).views.length;
|
||||
return state.session.viewer.views.length;
|
||||
},
|
||||
|
||||
webxr_getViewPose: function(index, position, orientation) {
|
||||
if (!state.frame) {
|
||||
if (!state.session || !state.session.viewer) {
|
||||
return false;
|
||||
}
|
||||
var view = getViewerPose(state.frame).views[index];
|
||||
if (view) {
|
||||
HEAPF32[position >> 2 + 0] = view.transform.position.x;
|
||||
HEAPF32[position >> 2 + 1] = view.transform.position.y;
|
||||
HEAPF32[position >> 2 + 2] = view.transform.position.z;
|
||||
HEAPF32[position >> 2 + 3] = view.transform.position.w;
|
||||
HEAPF32[orientation >> 2 + 0] = view.transform.orientation.x;
|
||||
HEAPF32[orientation >> 2 + 1] = view.transform.orientation.y;
|
||||
HEAPF32[orientation >> 2 + 2] = view.transform.orientation.z;
|
||||
HEAPF32[orientation >> 2 + 3] = view.transform.orientation.w;
|
||||
|
||||
var view = state.session.viewer.views[index];
|
||||
if (state.session.viewer.views[index]) {
|
||||
var transform = view.transform;
|
||||
HEAPF32[position >> 2 + 0] = transform.position.x;
|
||||
HEAPF32[position >> 2 + 1] = transform.position.y;
|
||||
HEAPF32[position >> 2 + 2] = transform.position.z;
|
||||
HEAPF32[position >> 2 + 3] = transform.position.w;
|
||||
HEAPF32[orientation >> 2 + 0] = transform.orientation.x;
|
||||
HEAPF32[orientation >> 2 + 1] = transform.orientation.y;
|
||||
HEAPF32[orientation >> 2 + 2] = transform.orientation.z;
|
||||
HEAPF32[orientation >> 2 + 3] = transform.orientation.w;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
webxr_getViewAngles: function(index, left, right, up, down) {
|
||||
return false;
|
||||
return false; // TODO
|
||||
},
|
||||
|
||||
webxr_getClipDistance: function(clipNear, clipFar) {
|
||||
|
@ -160,6 +260,7 @@ var webxr = {
|
|||
},
|
||||
|
||||
webxr_setClipDistance: function(clipNear, clipFar) {
|
||||
if (!state.session) return;
|
||||
state.clipNear = clipNear;
|
||||
state.clipFar = clipFar;
|
||||
state.session.updateRenderState({
|
||||
|
@ -169,40 +270,144 @@ var webxr = {
|
|||
},
|
||||
|
||||
webxr_getBoundsDimensions: function(width, depth) {
|
||||
HEAPF32[width >> 2] = 0.0;
|
||||
HEAPF32[width >> 2] = 0.0; // Unsupported, see #557
|
||||
HEAPF32[depth >> 2] = 0.0;
|
||||
},
|
||||
|
||||
webxr_getBoundsGeometry: function(count) {
|
||||
return 0; /* NULL */ // TODO
|
||||
if (!state.session || !(state.session.space instanceof XRBoundedReferenceSpace)) {
|
||||
return 0; /* NULL */
|
||||
}
|
||||
|
||||
var points = state.session.space.boundsGeometry;
|
||||
|
||||
if (state.boundsGeometryCount < points.length) {
|
||||
Module._free(state.boundsGeometry|0);
|
||||
state.boundsGeometry = Module._malloc(4 * 4 * points.length);
|
||||
if (state.boundsGeometry === 0) {
|
||||
return state.boundsGeometry;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
HEAPF32.set(points[i], state.boundsGeometry + 4 * i);
|
||||
}
|
||||
|
||||
return state.boundsGeometry;
|
||||
},
|
||||
|
||||
webxr_getPose: function(device, position, orientation) {
|
||||
return false; // TODO
|
||||
if (!state.session || !state.session.viewer) return false;
|
||||
|
||||
if (device === 0 /* DEVICE_HEAD */) {
|
||||
var transform = state.session.viewer.transform;
|
||||
HEAPF32[position >> 2 + 0] = transform.position.x;
|
||||
HEAPF32[position >> 2 + 1] = transform.position.y;
|
||||
HEAPF32[position >> 2 + 2] = transform.position.z;
|
||||
HEAPF32[position >> 2 + 3] = transform.position.w;
|
||||
HEAPF32[orientation >> 2 + 0] = transform.orientation.x;
|
||||
HEAPF32[orientation >> 2 + 1] = transform.orientation.y;
|
||||
HEAPF32[orientation >> 2 + 2] = transform.orientation.z;
|
||||
HEAPF32[orientation >> 2 + 3] = transform.orientation.w;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.session.inputSources[device]) {
|
||||
var inputSource = state.session.inputSources[device];
|
||||
var space = inputSource.gripSpace || inputSource.targetRaySpace;
|
||||
var transform = state.session.frame.getPose(space, state.session.space).transform;
|
||||
HEAPF32[position >> 2 + 0] = transform.position.x;
|
||||
HEAPF32[position >> 2 + 1] = transform.position.y;
|
||||
HEAPF32[position >> 2 + 2] = transform.position.z;
|
||||
HEAPF32[position >> 2 + 3] = transform.position.w;
|
||||
HEAPF32[orientation >> 2 + 0] = transform.orientation.x;
|
||||
HEAPF32[orientation >> 2 + 1] = transform.orientation.y;
|
||||
HEAPF32[orientation >> 2 + 2] = transform.orientation.z;
|
||||
HEAPF32[orientation >> 2 + 3] = transform.orientation.w;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
webxr_getVelocity: function(device, velocity, angularVelocity) {
|
||||
return false; // TODO
|
||||
return false; // Unsupported, see #619
|
||||
},
|
||||
|
||||
webxr_isDown: function(device, button, down, changed) {
|
||||
return false; // TODO
|
||||
if (!state.session) return false;
|
||||
|
||||
var inputSource = state.session.inputSources[device];
|
||||
if (!inputSource || !inputSource.gamepad || !inputSource.mapping || !inputSource.mapping[button]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HEAPU32[down >> 2] = inputSource.gamepad.buttons[inputSource.mapping[button]].pressed ? 1 : 0;
|
||||
HEAPU32[changed >> 2] = 0; // TODO
|
||||
return true;
|
||||
},
|
||||
|
||||
webxr_isTouched: function(device, button, touched) {
|
||||
return false; // TODO
|
||||
if (!state.session) return false;
|
||||
|
||||
var inputSource = state.session.inputSources[device];
|
||||
if (!inputSource || !inputSource.gamepad || !inputSource.mapping || !inputSource.mapping[button]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HEAPU32[touched >> 2] = inputSource.gamepad.buttons[inputSource.mapping[button]].touched ? 1 : 0;
|
||||
return true;
|
||||
},
|
||||
|
||||
webxr_getAxis: function(device, axis, value) {
|
||||
return false; // TODO
|
||||
if (!state.session) return false;
|
||||
|
||||
var inputSource = state.session.inputSources[device];
|
||||
if (!inputSource || !inputSource.gamepad || !inputSource.mapping) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (axis) {
|
||||
// These 1D axes are queried as buttons in the Gamepad API
|
||||
// The DeviceAxis enumerants match the DeviceButton ones, so they're interchangeable
|
||||
case 0: /* AXIS_TRIGGER */
|
||||
case 3: /* AXIS_GRIP */
|
||||
if (inputSource.mapping[axis]) {
|
||||
HEAPF32[value >> 2] = inputSource.gamepad.buttons[inputSource.mapping[axis]].value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case 1: /* AXIS_THUMBSTICK */
|
||||
HEAPF32[value >> 2 + 0] = inputSource.gamepad.axes[2];
|
||||
HEAPF32[value >> 2 + 1] = inputSource.gamepad.axes[3];
|
||||
return true;
|
||||
|
||||
case 2: /* AXIS_TOUCHPAD */
|
||||
HEAPF32[value >> 2 + 0] = inputSource.gamepad.axes[0];
|
||||
HEAPF32[value >> 2 + 1] = inputSource.gamepad.axes[1];
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
webxr_vibrate: function(device, strength, duration, frequency) {
|
||||
return false; // TODO
|
||||
if (!state.session) return false;
|
||||
|
||||
var inputSource = state.session.inputSources[device];
|
||||
if (!inputSource || !inputSource.gamepad || !inputSource.gamepad.hapticActuators || !inputSource.gamepad.hapticActuators[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not technically an official WebXR feature, but widely supported
|
||||
inputSource.gamepad.hapticActuators[0].pulse(strength, duration * 1000);
|
||||
return true;
|
||||
},
|
||||
|
||||
webxr_newModelData: function(device, animated) {
|
||||
return 0; /* NULL */ // TODO
|
||||
return 0; /* NULL */
|
||||
},
|
||||
|
||||
webxr_animate: function(device, model) {
|
||||
|
|
Loading…
Reference in New Issue