#include "api.h" #include "headset/headset.h" #include "data/modelData.h" #include "graphics/graphics.h" #include "core/maf.h" #include StringEntry lovrHeadsetDriver[] = { [DRIVER_SIMULATOR] = ENTRY("desktop"), [DRIVER_OPENXR] = ENTRY("openxr"), [DRIVER_WEBXR] = ENTRY("webxr"), { 0 } }; StringEntry lovrPassthroughMode[] = { [PASSTHROUGH_OPAQUE] = ENTRY("opaque"), [PASSTHROUGH_BLEND] = ENTRY("blend"), [PASSTHROUGH_ADD] = ENTRY("add"), { 0 } }; StringEntry lovrDevice[] = { [DEVICE_HEAD] = ENTRY("head"), [DEVICE_FLOOR] = ENTRY("floor"), [DEVICE_HAND_LEFT] = ENTRY("hand/left"), [DEVICE_HAND_RIGHT] = ENTRY("hand/right"), [DEVICE_HAND_LEFT_GRIP] = ENTRY("hand/left/grip"), [DEVICE_HAND_RIGHT_GRIP] = ENTRY("hand/right/grip"), [DEVICE_HAND_LEFT_POINT] = ENTRY("hand/left/point"), [DEVICE_HAND_RIGHT_POINT] = ENTRY("hand/right/point"), [DEVICE_HAND_LEFT_PINCH] = ENTRY("hand/left/pinch"), [DEVICE_HAND_RIGHT_PINCH] = ENTRY("hand/right/pinch"), [DEVICE_HAND_LEFT_POKE] = ENTRY("hand/left/poke"), [DEVICE_HAND_RIGHT_POKE] = ENTRY("hand/right/poke"), [DEVICE_ELBOW_LEFT] = ENTRY("elbow/left"), [DEVICE_ELBOW_RIGHT] = ENTRY("elbow/right"), [DEVICE_SHOULDER_LEFT] = ENTRY("shoulder/left"), [DEVICE_SHOULDER_RIGHT] = ENTRY("shoulder/right"), [DEVICE_CHEST] = ENTRY("chest"), [DEVICE_WAIST] = ENTRY("waist"), [DEVICE_KNEE_LEFT] = ENTRY("knee/left"), [DEVICE_KNEE_RIGHT] = ENTRY("knee/right"), [DEVICE_FOOT_LEFT] = ENTRY("foot/left"), [DEVICE_FOOT_RIGHT] = ENTRY("foot/right"), [DEVICE_CAMERA] = ENTRY("camera"), [DEVICE_KEYBOARD] = ENTRY("keyboard"), [DEVICE_EYE_LEFT] = ENTRY("eye/left"), [DEVICE_EYE_RIGHT] = ENTRY("eye/right"), [DEVICE_EYE_GAZE] = ENTRY("eye/gaze"), { 0 } }; StringEntry lovrDeviceButton[] = { [BUTTON_TRIGGER] = ENTRY("trigger"), [BUTTON_THUMBSTICK] = ENTRY("thumbstick"), [BUTTON_THUMBREST] = ENTRY("thumbrest"), [BUTTON_TOUCHPAD] = ENTRY("touchpad"), [BUTTON_GRIP] = ENTRY("grip"), [BUTTON_MENU] = ENTRY("menu"), [BUTTON_A] = ENTRY("a"), [BUTTON_B] = ENTRY("b"), [BUTTON_X] = ENTRY("x"), [BUTTON_Y] = ENTRY("y"), [BUTTON_PROXIMITY] = ENTRY("proximity"), { 0 } }; StringEntry lovrDeviceAxis[] = { [AXIS_TRIGGER] = ENTRY("trigger"), [AXIS_THUMBSTICK] = ENTRY("thumbstick"), [AXIS_TOUCHPAD] = ENTRY("touchpad"), [AXIS_GRIP] = ENTRY("grip"), { 0 } }; StringEntry lovrViewMask[] = { [EYE_BOTH] = ENTRY("both"), [EYE_LEFT] = ENTRY("left"), [EYE_RIGHT] = ENTRY("right"), { 0 } }; static Device luax_optdevice(lua_State* L, int index) { const char* str = luaL_optstring(L, 1, "head"); if (!strcmp(str, "left")) { return DEVICE_HAND_LEFT; } else if (!strcmp(str, "right")) { return DEVICE_HAND_RIGHT; } return luax_checkenum(L, 1, Device, "head"); } static int l_lovrHeadsetStart(lua_State* L) { lovrHeadsetInterface->start(); return 0; } static int l_lovrHeadsetGetDriver(lua_State* L) { luax_pushenum(L, HeadsetDriver, lovrHeadsetInterface->driverType); char name[256]; if (lovrHeadsetInterface->getDriverName(name, sizeof(name))) { lua_pushstring(L, name); } else { lua_pushnil(L); } return 2; } static int l_lovrHeadsetGetName(lua_State* L) { char name[256]; if (lovrHeadsetInterface->getName(name, sizeof(name))) { lua_pushstring(L, name); } else { lua_pushnil(L); } return 1; } static int l_lovrHeadsetIsSeated(lua_State* L) { lua_pushboolean(L, lovrHeadsetInterface->isSeated()); return 1; } static int l_lovrHeadsetGetDisplayWidth(lua_State* L) { uint32_t width, height; lovrHeadsetInterface->getDisplayDimensions(&width, &height); lua_pushinteger(L, width); return 1; } static int l_lovrHeadsetGetDisplayHeight(lua_State* L) { uint32_t width, height; lovrHeadsetInterface->getDisplayDimensions(&width, &height); lua_pushinteger(L, height); return 1; } static int l_lovrHeadsetGetDisplayDimensions(lua_State* L) { uint32_t width, height; lovrHeadsetInterface->getDisplayDimensions(&width, &height); lua_pushinteger(L, width); lua_pushinteger(L, height); return 2; } static int l_lovrHeadsetGetRefreshRate(lua_State* L) { float refreshRate = lovrHeadsetInterface->getRefreshRate ? lovrHeadsetInterface->getRefreshRate() : 0.f; if (refreshRate == 0.f) { lua_pushnil(L); } else { lua_pushnumber(L, refreshRate); } return 1; } static int l_lovrHeadsetSetRefreshRate(lua_State* L) { float refreshRate = luax_checkfloat(L, 1); bool success = lovrHeadsetInterface->setRefreshRate ? lovrHeadsetInterface->setRefreshRate(refreshRate) : false; lua_pushboolean(L, success); return 1; } static int l_lovrHeadsetGetRefreshRates(lua_State* L) { uint32_t count; const float* refreshRates = lovrHeadsetInterface->getRefreshRates(&count); if (!refreshRates) { lua_pushnil(L); } else { lua_settop(L, 0); lua_createtable(L, count, 0); for (uint32_t i = 0; i < count; ++i) { lua_pushnumber(L, refreshRates[i]); lua_rawseti(L, 1, i + 1); } } return 1; } static int l_lovrHeadsetGetPassthrough(lua_State* L) { PassthroughMode mode = lovrHeadsetInterface->getPassthrough(); luax_pushenum(L, PassthroughMode, mode); return 1; } static int l_lovrHeadsetSetPassthrough(lua_State* L) { PassthroughMode mode; if (lua_isnoneornil(L, 1)) { mode = PASSTHROUGH_DEFAULT; } else if (lua_isboolean(L, 1)) { mode = lua_toboolean(L, 1) ? PASSTHROUGH_TRANSPARENT : PASSTHROUGH_OPAQUE; } else { mode = luax_checkenum(L, 1, PassthroughMode, NULL); } bool success = lovrHeadsetInterface->setPassthrough(mode); lua_pushboolean(L, success); return 1; } static int l_lovrHeadsetGetPassthroughModes(lua_State* L) { lua_createtable(L, 0, 3); for (int i = 0; lovrPassthroughMode[i].length > 0; i++) { lua_pushlstring(L, lovrPassthroughMode[i].string, lovrPassthroughMode[i].length); lua_pushboolean(L, lovrHeadsetInterface->isPassthroughSupported(i)); lua_settable(L, -3); } return 1; } static int l_lovrHeadsetGetViewCount(lua_State* L) { lua_pushinteger(L, lovrHeadsetInterface->getViewCount()); return 1; } static int l_lovrHeadsetGetViewPose(lua_State* L) { float position[3], orientation[4]; uint32_t view = luax_checku32(L, 1) - 1; if (!lovrHeadsetInterface->getViewPose(view, position, orientation)) { lua_pushnil(L); return 1; } float angle, ax, ay, az; quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); lua_pushnumber(L, position[0]); lua_pushnumber(L, position[1]); lua_pushnumber(L, position[2]); lua_pushnumber(L, angle); lua_pushnumber(L, ax); lua_pushnumber(L, ay); lua_pushnumber(L, az); return 7; } static int l_lovrHeadsetGetViewAngles(lua_State* L) { float left, right, up, down; uint32_t view = luax_checku32(L, 1) - 1; if (!lovrHeadsetInterface->getViewAngles(view, &left, &right, &up, &down)) { lua_pushnil(L); return 1; } lua_pushnumber(L, left); lua_pushnumber(L, right); lua_pushnumber(L, up); lua_pushnumber(L, down); return 4; } static int l_lovrHeadsetGetClipDistance(lua_State* L) { float clipNear, clipFar; lovrHeadsetInterface->getClipDistance(&clipNear, &clipFar); lua_pushnumber(L, clipNear); lua_pushnumber(L, clipFar); return 2; } static int l_lovrHeadsetSetClipDistance(lua_State* L) { float clipNear = luax_checkfloat(L, 1); float clipFar = luax_checkfloat(L, 2); lovrHeadsetInterface->setClipDistance(clipNear, clipFar); return 0; } static int l_lovrHeadsetGetBoundsWidth(lua_State* L) { float width, depth; lovrHeadsetInterface->getBoundsDimensions(&width, &depth); lua_pushnumber(L, width); return 1; } static int l_lovrHeadsetGetBoundsDepth(lua_State* L) { float width, depth; lovrHeadsetInterface->getBoundsDimensions(&width, &depth); lua_pushnumber(L, depth); return 1; } static int l_lovrHeadsetGetBoundsDimensions(lua_State* L) { float width, depth; lovrHeadsetInterface->getBoundsDimensions(&width, &depth); lua_pushnumber(L, width); lua_pushnumber(L, depth); return 2; } static int l_lovrHeadsetGetBoundsGeometry(lua_State* L) { uint32_t count; const float* points = lovrHeadsetInterface->getBoundsGeometry(&count); if (!points) { lua_pushnil(L); return 1; } if (lua_type(L, 1) == LUA_TTABLE) { lua_settop(L, 1); } else { lua_settop(L, 0); lua_createtable(L, count / 4, 0); } int j = 1; for (uint32_t i = 0; i < count; i += 4) { lua_pushnumber(L, points[i + 0]); lua_rawseti(L, 1, j++); lua_pushnumber(L, points[i + 1]); lua_rawseti(L, 1, j++); lua_pushnumber(L, points[i + 2]); lua_rawseti(L, 1, j++); } return 1; } static int l_lovrHeadsetIsTracked(lua_State* L) { Device device = luax_optdevice(L, 1); float position[3], orientation[4]; lua_pushboolean(L, lovrHeadsetInterface->getPose(device, position, orientation)); return 1; } static int l_lovrHeadsetGetPose(lua_State* L) { Device device = luax_optdevice(L, 1); float position[3], orientation[4]; if (lovrHeadsetInterface->getPose(device, position, orientation)) { float angle, ax, ay, az; quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); lua_pushnumber(L, position[0]); lua_pushnumber(L, position[1]); lua_pushnumber(L, position[2]); lua_pushnumber(L, angle); lua_pushnumber(L, ax); lua_pushnumber(L, ay); lua_pushnumber(L, az); return 7; } for (int i = 0; i < 7; i++) { lua_pushnumber(L, 0.); } return 7; } static int l_lovrHeadsetGetPosition(lua_State* L) { Device device = luax_optdevice(L, 1); float position[3], orientation[4]; if (lovrHeadsetInterface->getPose(device, position, orientation)) { lua_pushnumber(L, position[0]); lua_pushnumber(L, position[1]); lua_pushnumber(L, position[2]); return 3; } for (int i = 0; i < 3; i++) { lua_pushnumber(L, 0.); } return 3; } static int l_lovrHeadsetGetOrientation(lua_State* L) { Device device = luax_optdevice(L, 1); float position[3], orientation[4]; if (lovrHeadsetInterface->getPose(device, position, orientation)) { float angle, ax, ay, az; quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); lua_pushnumber(L, angle); lua_pushnumber(L, ax); lua_pushnumber(L, ay); lua_pushnumber(L, az); return 4; } for (int i = 0; i < 4; i++) { lua_pushnumber(L, 0.); } return 4; } static int l_lovrHeadsetGetDirection(lua_State* L) { Device device = luax_optdevice(L, 1); float position[3], orientation[4]; if (lovrHeadsetInterface->getPose(device, position, orientation)) { float direction[3]; quat_getDirection(orientation, direction); lua_pushnumber(L, direction[0]); lua_pushnumber(L, direction[1]); lua_pushnumber(L, direction[2]); return 3; } for (int i = 0; i < 3; i++) { lua_pushnumber(L, 0.); } return 3; } static int l_lovrHeadsetGetVelocity(lua_State* L) { Device device = luax_optdevice(L, 1); float velocity[3], angularVelocity[3]; if (lovrHeadsetInterface->getVelocity(device, velocity, angularVelocity)) { lua_pushnumber(L, velocity[0]); lua_pushnumber(L, velocity[1]); lua_pushnumber(L, velocity[2]); return 3; } for (int i = 0; i < 3; i++) { lua_pushnumber(L, 0.); } return 3; } static int l_lovrHeadsetGetAngularVelocity(lua_State* L) { Device device = luax_optdevice(L, 1); float velocity[3], angularVelocity[3]; if (lovrHeadsetInterface->getVelocity(device, velocity, angularVelocity)) { lua_pushnumber(L, angularVelocity[0]); lua_pushnumber(L, angularVelocity[1]); lua_pushnumber(L, angularVelocity[2]); return 3; } for (int i = 0; i < 3; i++) { lua_pushnumber(L, 0.); } return 3; } static int l_lovrHeadsetIsDown(lua_State* L) { Device device = luax_optdevice(L, 1); DeviceButton button = luax_checkenum(L, 2, DeviceButton, NULL); bool down, changed; if (lovrHeadsetInterface->isDown(device, button, &down, &changed)) { lua_pushboolean(L, down); return 1; } lua_pushnil(L); return 1; } static int l_lovrHeadsetWasPressed(lua_State* L) { Device device = luax_optdevice(L, 1); DeviceButton button = luax_checkenum(L, 2, DeviceButton, NULL); bool down, changed; if (lovrHeadsetInterface->isDown(device, button, &down, &changed)) { lua_pushboolean(L, down && changed); return 1; } lua_pushboolean(L, false); return 1; } static int l_lovrHeadsetWasReleased(lua_State* L) { Device device = luax_optdevice(L, 1); DeviceButton button = luax_checkenum(L, 2, DeviceButton, NULL); bool down, changed; if (lovrHeadsetInterface->isDown(device, button, &down, &changed)) { lua_pushboolean(L, !down && changed); return 1; } lua_pushboolean(L, false); return 1; } static int l_lovrHeadsetIsTouched(lua_State* L) { Device device = luax_optdevice(L, 1); DeviceButton button = luax_checkenum(L, 2, DeviceButton, NULL); bool touched; if (lovrHeadsetInterface->isTouched(device, button, &touched)) { lua_pushboolean(L, touched); return 1; } lua_pushnil(L); return 1; } static const int axisCounts[MAX_AXES] = { [AXIS_TRIGGER] = 1, [AXIS_THUMBSTICK] = 2, [AXIS_TOUCHPAD] = 2, [AXIS_GRIP] = 1 }; static int l_lovrHeadsetGetAxis(lua_State* L) { Device device = luax_optdevice(L, 1); DeviceAxis axis = luax_checkenum(L, 2, DeviceAxis, NULL); int count = axisCounts[axis]; float value[4]; if (lovrHeadsetInterface->getAxis(device, axis, value)) { for (int i = 0; i < count; i++) { lua_pushnumber(L, value[i]); } return count; } for (int i = 0; i < count; i++) { lua_pushnumber(L, 0.); } return count; } static int l_lovrHeadsetGetSkeleton(lua_State* L) { Device device = luax_optdevice(L, 1); float poses[HAND_JOINT_COUNT * 8]; if (lovrHeadsetInterface->getSkeleton(device, poses)) { if (!lua_istable(L, 2)) { lua_createtable(L, HAND_JOINT_COUNT, 0); } else { lua_settop(L, 2); } for (uint32_t i = 0; i < HAND_JOINT_COUNT; i++) { lua_createtable(L, 8, 0); float angle, ax, ay, az; float* pose = poses + i * 8; quat_getAngleAxis(pose + 4, &angle, &ax, &ay, &az); lua_pushnumber(L, pose[0]); lua_pushnumber(L, pose[1]); lua_pushnumber(L, pose[2]); lua_pushnumber(L, pose[3]); lua_pushnumber(L, angle); lua_pushnumber(L, ax); lua_pushnumber(L, ay); lua_pushnumber(L, az); lua_rawseti(L, -9, 8); lua_rawseti(L, -8, 7); lua_rawseti(L, -7, 6); lua_rawseti(L, -6, 5); lua_rawseti(L, -5, 4); lua_rawseti(L, -4, 3); lua_rawseti(L, -3, 2); lua_rawseti(L, -2, 1); lua_pushnumber(L, pose[3]); lua_setfield(L, -2, "radius"); lua_rawseti(L, -2, i + 1); } return 1; } lua_pushnil(L); return 1; } static int l_lovrHeadsetVibrate(lua_State* L) { Device device = luax_optdevice(L, 1); float strength = luax_optfloat(L, 2, 1.f); float duration = luax_optfloat(L, 3, .5f); float frequency = luax_optfloat(L, 4, 0.f); if (lovrHeadsetInterface->vibrate(device, strength, duration, frequency)) { lua_pushboolean(L, true); return 1; } lua_pushboolean(L, false); return 1; } static int l_lovrHeadsetStopVibration(lua_State* L) { Device device = luax_optdevice(L, 1); lovrHeadsetInterface->stopVibration(device); return 0; } static int l_lovrHeadsetNewModel(lua_State* L) { Device device = luax_optdevice(L, 1); bool animated = false; if (lua_istable(L, 2)) { lua_getfield(L, 2, "animated"); animated = lua_toboolean(L, -1); lua_pop(L, 1); } ModelData* modelData = lovrHeadsetInterface->newModelData(device, animated); if (modelData) { ModelInfo info = { .data = modelData, .mipmaps = true }; uint32_t defer = lovrDeferPush(); lovrDeferRelease(info.data, lovrModelDataDestroy); Model* model = lovrModelCreate(&info); luax_pushtype(L, Model, model); lovrRelease(model, lovrModelDestroy); lovrDeferPop(defer); return 1; } return 0; } static int l_lovrHeadsetAnimate(lua_State* L) { Model* model = luax_checktype(L, 1, Model); lua_pushboolean(L, lovrHeadsetInterface->animate(model)); return 1; } static int l_lovrHeadsetGetLayers(lua_State* L) { uint32_t count; Layer** layers = lovrHeadsetInterface->getLayers(&count); lua_createtable(L, (int) count, 0); for (uint32_t i = 0; i < count; i++) { luax_pushtype(L, Layer, layers[i]); lua_rawseti(L, -2, (int) i + 1); } return 1; } static int l_lovrHeadsetSetLayers(lua_State* L) { Layer* layers[MAX_LAYERS]; uint32_t count = 0; if (lua_type(L, 1) == LUA_TTABLE) { count = luax_len(L, 1); lovrCheck(count <= MAX_LAYERS, "Too many layers (max is %d)", MAX_LAYERS); for (uint32_t i = 0; i < count; i++) { lua_rawgeti(L, 1, (int) i + 1); layers[i] = luax_checktype(L, -1, Layer); lua_pop(L, 1); } } else { count = lua_gettop(L); lovrCheck(count <= MAX_LAYERS, "Too many layers (max is %d)", MAX_LAYERS); for (uint32_t i = 0; i < count; i++) { layers[i] = luax_checktype(L, (int) i + 1, Layer); } } lovrHeadsetInterface->setLayers(layers, count); return 0; } static int l_lovrHeadsetNewLayer(lua_State* L) { uint32_t width = luax_checku32(L, 1); uint32_t height = luax_checku32(L, 2); Layer* layer = lovrHeadsetInterface->newLayer(width, height); luax_pushtype(L, Layer, layer); lovrRelease(layer, lovrHeadsetInterface->destroyLayer); return 1; } static int l_lovrHeadsetGetTexture(lua_State* L) { Texture* texture = lovrHeadsetInterface->getTexture(); luax_pushtype(L, Texture, texture); return 1; } static int l_lovrHeadsetGetPass(lua_State* L) { Pass* pass = lovrHeadsetInterface->getPass(); luax_pushtype(L, Pass, pass); return 1; } static int l_lovrHeadsetSubmit(lua_State* L) { lovrHeadsetInterface->submit(); return 0; } static int l_lovrHeadsetIsVisible(lua_State* L) { lua_pushboolean(L, lovrHeadsetInterface->isVisible()); return 1; } static int l_lovrHeadsetIsFocused(lua_State* L) { lua_pushboolean(L, lovrHeadsetInterface->isFocused()); return 1; } static int l_lovrHeadsetUpdate(lua_State* L) { double dt = 0.; if (lovrHeadsetInterface->update) { dt = lovrHeadsetInterface->update(); } lua_pushnumber(L, dt); return 1; } static int l_lovrHeadsetGetTime(lua_State* L) { lua_pushnumber(L, lovrHeadsetInterface->getDisplayTime()); return 1; } static int l_lovrHeadsetGetDeltaTime(lua_State* L) { lua_pushnumber(L, lovrHeadsetInterface->getDeltaTime()); return 1; } static int l_lovrHeadsetGetHands(lua_State* L) { if (lua_istable(L, 1)) { lua_settop(L, 1); } else { lua_newtable(L); } int count = 0; float position[3], orientation[4]; Device hands[] = { DEVICE_HAND_LEFT, DEVICE_HAND_RIGHT }; for (size_t i = 0; i < COUNTOF(hands); i++) { if (lovrHeadsetInterface->getPose(hands[i], position, orientation)) { luax_pushenum(L, Device, hands[i]); lua_rawseti(L, -2, ++count); } } lua_pushnil(L); lua_rawseti(L, -2, ++count); return 1; } static const luaL_Reg lovrHeadset[] = { { "start", l_lovrHeadsetStart }, { "getDriver", l_lovrHeadsetGetDriver }, { "getName", l_lovrHeadsetGetName }, { "getDisplayWidth", l_lovrHeadsetGetDisplayWidth }, { "getDisplayHeight", l_lovrHeadsetGetDisplayHeight }, { "getDisplayDimensions", l_lovrHeadsetGetDisplayDimensions }, { "getRefreshRate", l_lovrHeadsetGetRefreshRate }, { "setRefreshRate", l_lovrHeadsetSetRefreshRate }, { "getRefreshRates", l_lovrHeadsetGetRefreshRates }, { "getPassthrough", l_lovrHeadsetGetPassthrough }, { "setPassthrough", l_lovrHeadsetSetPassthrough }, { "getPassthroughModes", l_lovrHeadsetGetPassthroughModes }, { "getViewCount", l_lovrHeadsetGetViewCount }, { "getViewPose", l_lovrHeadsetGetViewPose }, { "getViewAngles", l_lovrHeadsetGetViewAngles }, { "getClipDistance", l_lovrHeadsetGetClipDistance }, { "setClipDistance", l_lovrHeadsetSetClipDistance }, { "isSeated", l_lovrHeadsetIsSeated }, { "getBoundsWidth", l_lovrHeadsetGetBoundsWidth }, { "getBoundsDepth", l_lovrHeadsetGetBoundsDepth }, { "getBoundsDimensions", l_lovrHeadsetGetBoundsDimensions }, { "getBoundsGeometry", l_lovrHeadsetGetBoundsGeometry }, { "isTracked", l_lovrHeadsetIsTracked }, { "getPose", l_lovrHeadsetGetPose }, { "getPosition", l_lovrHeadsetGetPosition }, { "getOrientation", l_lovrHeadsetGetOrientation }, { "getDirection", l_lovrHeadsetGetDirection }, { "getVelocity", l_lovrHeadsetGetVelocity }, { "getAngularVelocity", l_lovrHeadsetGetAngularVelocity }, { "isDown", l_lovrHeadsetIsDown }, { "wasPressed", l_lovrHeadsetWasPressed }, { "wasReleased", l_lovrHeadsetWasReleased }, { "isTouched", l_lovrHeadsetIsTouched }, { "getAxis", l_lovrHeadsetGetAxis }, { "getSkeleton", l_lovrHeadsetGetSkeleton }, { "vibrate", l_lovrHeadsetVibrate }, { "stopVibration", l_lovrHeadsetStopVibration }, { "newModel", l_lovrHeadsetNewModel }, { "animate", l_lovrHeadsetAnimate }, { "newLayer", l_lovrHeadsetNewLayer }, { "getLayers", l_lovrHeadsetGetLayers }, { "setLayers", l_lovrHeadsetSetLayers }, { "getTexture", l_lovrHeadsetGetTexture }, { "getPass", l_lovrHeadsetGetPass }, { "submit", l_lovrHeadsetSubmit }, { "isVisible", l_lovrHeadsetIsVisible }, { "isFocused", l_lovrHeadsetIsFocused }, { "update", l_lovrHeadsetUpdate }, { "getTime", l_lovrHeadsetGetTime }, { "getDeltaTime", l_lovrHeadsetGetDeltaTime }, { "getHands", l_lovrHeadsetGetHands }, { NULL, NULL } }; extern const luaL_Reg lovrLayer[]; int luaopen_lovr_headset(lua_State* L) { lua_newtable(L); luax_register(L, lovrHeadset); luax_registertype(L, Layer); HeadsetDriver drivers[8]; HeadsetConfig config = { .drivers = drivers, .driverCount = 0, .supersample = 1.f, .seated = false, .stencil = false, .antialias = true, .submitDepth = true, .overlay = false, .overlayOrder = 0 }; luax_pushconf(L); if (lua_istable(L, -1)) { lua_getfield(L, -1, "headset"); if (lua_istable(L, -1)) { lua_getfield(L, -1, "drivers"); int n = luax_len(L, -1); for (int i = 0; i < n; i++) { lua_rawgeti(L, -1, i + 1); config.drivers[config.driverCount++] = luax_checkenum(L, -1, HeadsetDriver, NULL); lovrCheck(config.driverCount < COUNTOF(drivers), "Too many headset drivers specified in conf.lua"); lua_pop(L, 1); } lua_pop(L, 1); lua_getfield(L, -1, "supersample"); if (lua_type(L, -1) == LUA_TBOOLEAN) { config.supersample = lua_toboolean(L, -1) ? 2.f : 1.f; } else { config.supersample = luax_optfloat(L, -1, 1.f); } lua_pop(L, 1); lua_getfield(L, -1, "seated"); config.seated = lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "stencil"); config.stencil = lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "antialias"); config.antialias = lua_isnil(L, -1) ? true : lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "submitdepth"); config.submitDepth = lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "overlay"); config.overlay = lua_toboolean(L, -1); config.overlayOrder = lua_type(L, -1) == LUA_TNUMBER ? luax_optu32(L, -1, 0) : 0; lua_pop(L, 1); } lua_pop(L, 1); } lua_pop(L, 1); luax_atexit(L, lovrHeadsetDestroy); lovrHeadsetInit(&config); return 1; }