og-revive/Revive/InputManager.cpp

809 lines
25 KiB
C++
Raw Permalink Normal View History

#include "InputManager.h"
#include "Session.h"
#include "SessionDetails.h"
#include "Settings.h"
#include "SettingsManager.h"
#include "CompositorBase.h"
#include "OVR_CAPI.h"
#include "REV_Math.h"
#include <openvr.h>
#include <Windows.h>
#include <Xinput.h>
#include <lua.hpp>
#include <assert.h>
2017-12-22 16:41:11 +01:00
const char* InputManager::s_ButtonNames[vr::k_EButton_Max];
const char* InputManager::s_TypeNames[4];
2017-12-22 16:28:38 +01:00
InputManager::InputManager()
: m_InputDevices()
, m_LastPoses()
{
for (ovrPoseStatef& pose : m_LastPoses)
pose.ThePose = OVR::Posef::Identity();
// TODO: This might change if a new HMD is connected (unlikely)
m_fVsyncToPhotons = vr::VRSystem()->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
2017-12-22 16:28:38 +01:00
for (int i = 0; i < vr::k_EButton_Max; i++)
2017-12-22 16:41:11 +01:00
s_ButtonNames[i] = vr::VRSystem()->GetButtonIdNameFromEnum((vr::EVRButtonId)i) + 10; // Skip the 10-char enum prefix
for (int i = 0; i < 4; i++)
s_TypeNames[i] = vr::VRSystem()->GetControllerAxisTypeNameFromEnum((vr::EVRControllerAxisType)i) + +18; // Skip the 18-char enum prefix
2017-12-22 16:28:38 +01:00
// TODO: XInput is slow, move it to another thread
#if 0
m_InputDevices.push_back(new XboxGamepad());
#endif
m_InputDevices.push_back(new OculusRemote());
m_InputDevices.push_back(new OculusTouch(vr::TrackedControllerRole_LeftHand));
m_InputDevices.push_back(new OculusTouch(vr::TrackedControllerRole_RightHand));
UpdateConnectedControllers();
}
InputManager::~InputManager()
{
for (InputDevice* device : m_InputDevices)
delete device;
}
void InputManager::UpdateConnectedControllers()
{
uint32_t types = 0;
for (InputDevice* device : m_InputDevices)
{
if (device->IsConnected())
types |= device->GetType();
if (device->GetType() & ovrControllerType_Touch)
{
OculusTouch* touch = dynamic_cast<OculusTouch*>(device);
assert(touch);
if (!touch)
continue;
uint16_t axes = 0;
vr::TrackedDeviceIndex_t index = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(touch->GetRole());
for (int i = 0; i < vr::k_unControllerStateAxisCount; i++)
{
vr::EVRControllerAxisType type = (vr::EVRControllerAxisType)vr::VRSystem()->GetInt32TrackedDeviceProperty(
index, (vr::ETrackedDeviceProperty)(vr::Prop_Axis0Type_Int32 + i));
axes |= (type & 3) << (i * 2);
}
touch->m_AxisTypes = axes;
}
}
ConnectedControllers = types;
}
ovrResult InputManager::SetControllerVibration(ovrSession session, ovrControllerType controllerType, float frequency, float amplitude)
{
// Clamp the input
frequency = min(max(frequency, 0.0f), 1.0f);
amplitude = min(max(amplitude, 0.0f), 1.0f);
for (InputDevice* device : m_InputDevices)
{
if (controllerType & device->GetType() && ConnectedControllers & device->GetType())
device->SetVibration(frequency, amplitude);
}
return ovrSuccess;
}
// TODO: Make GetInputState() lock-free
ovrResult InputManager::GetInputState(ovrSession session, ovrControllerType controllerType, ovrInputState* inputState)
{
std::unique_lock<std::mutex> lk(m_InputMutex);
memset(inputState, 0, sizeof(ovrInputState));
inputState->TimeInSeconds = ovr_GetTimeInSeconds();
uint32_t types = 0;
for (InputDevice* device : m_InputDevices)
{
if (controllerType & device->GetType() && ConnectedControllers & device->GetType())
{
if (device->GetInputState(session, inputState))
types |= device->GetType();
}
}
inputState->ControllerType = (ovrControllerType)types;
return ovrSuccess;
}
ovrResult InputManager::SubmitControllerVibration(ovrSession session, ovrControllerType controllerType, const ovrHapticsBuffer* buffer)
{
for (InputDevice* device : m_InputDevices)
{
if (controllerType & device->GetType() && ConnectedControllers & device->GetType())
device->SubmitVibration(buffer);
}
return ovrSuccess;
}
ovrResult InputManager::GetControllerVibrationState(ovrSession session, ovrControllerType controllerType, ovrHapticsPlaybackState* outState)
{
memset(outState, 0, sizeof(ovrHapticsPlaybackState));
for (InputDevice* device : m_InputDevices)
{
if (controllerType & device->GetType() && ConnectedControllers & device->GetType())
device->GetVibrationState(outState);
}
return ovrSuccess;
}
ovrTouchHapticsDesc InputManager::GetTouchHapticsDesc(ovrControllerType controllerType)
{
ovrTouchHapticsDesc desc = { 0 };
if (controllerType & ovrControllerType_Touch)
{
desc.SampleRateHz = REV_HAPTICS_SAMPLE_RATE;
desc.SampleSizeInBytes = sizeof(uint8_t);
2017-08-21 20:46:30 +02:00
desc.SubmitMaxSamples = OVR_HAPTICS_BUFFER_SAMPLES_MAX;
desc.SubmitMinSamples = 1;
desc.SubmitOptimalSamples = 20;
desc.QueueMinSizeToAvoidStarvation = 5;
}
return desc;
}
unsigned int InputManager::TrackedDevicePoseToOVRStatusFlags(vr::TrackedDevicePose_t pose)
{
unsigned int result = 0;
if (pose.bPoseIsValid)
{
if (pose.bDeviceIsConnected)
result |= ovrStatus_OrientationTracked;
if (pose.eTrackingResult != vr::TrackingResult_Calibrating_OutOfRange &&
pose.eTrackingResult != vr::TrackingResult_Running_OutOfRange)
result |= ovrStatus_PositionTracked;
}
return result;
}
ovrPoseStatef InputManager::TrackedDevicePoseToOVRPose(vr::TrackedDevicePose_t pose, ovrPoseStatef& lastPose, double time)
{
ovrPoseStatef result = { OVR::Posef::Identity() };
if (!pose.bPoseIsValid)
return result;
OVR::Matrix4f matrix = REV::Matrix4f(pose.mDeviceToAbsoluteTracking);
// Make sure the orientation stays in the same hemisphere as the previous orientation, this prevents
// linear interpolations from suddenly flipping the long way around in Oculus Medium.
OVR::Quatf q(matrix);
q.EnsureSameHemisphere(lastPose.ThePose.Orientation);
result.ThePose.Orientation = q;
result.ThePose.Position = matrix.GetTranslation();
result.AngularVelocity = (REV::Vector3f)pose.vAngularVelocity;
result.LinearVelocity = (REV::Vector3f)pose.vVelocity;
result.AngularAcceleration = ((REV::Vector3f)pose.vAngularVelocity - lastPose.AngularVelocity) / float(time - lastPose.TimeInSeconds);
result.LinearAcceleration = ((REV::Vector3f)pose.vVelocity - lastPose.LinearVelocity) / float(time - lastPose.TimeInSeconds);
result.TimeInSeconds = time;
// Store the last pose
lastPose = result;
return result;
}
int InputManager::ErrorHandler(lua_State* L)
{
static bool first_error = true;
if (first_error)
vr::VROverlay()->ShowMessageOverlay(lua_tostring(L, -1), "Revive Input Error", "Ignore and Continue");
OutputDebugStringA(lua_tostring(L, -1));
OutputDebugStringA("\n");
lua_pop(L, 1);
first_error = false;
return 0;
}
extern HMODULE revModule; // TODO: Get rid of this
bool InputManager::LoadResourceScript(lua_State* L, const char* name)
{
HRSRC hRes = FindResourceA(revModule, name, "LUA");
DWORD dwSize = SizeofResource(revModule, hRes);
HGLOBAL hGlob = LoadResource(revModule, hRes);
const char* pData = reinterpret_cast<const char*>(::LockResource(hGlob));
if (luaL_loadbuffer(L, pData, dwSize, name))
{
OutputDebugStringA(lua_tostring(L, -1));
OutputDebugStringA("\n");
lua_pop(L, 1);
return false;
}
return !lua_pcall(L, 0, LUA_MULTRET, 1);
}
bool InputManager::LoadFileScript(lua_State* L, const char* fn)
{
if (luaL_loadfile(L, fn))
{
OutputDebugStringA(lua_tostring(L, -1));
OutputDebugStringA("\n");
lua_pop(L, 1);
return false;
}
return !lua_pcall(L, 0, LUA_MULTRET, 1);
}
bool InputManager::LoadInputScript(const char* fn)
{
for (InputDevice* device : m_InputDevices)
{
if (device->GetType() & ovrControllerType_Touch)
{
/* Create LUA VM state */
lua_State* L = luaL_newstate();
assert(L);
m_ScriptStates.push_back(L);
lua_pushcfunction(L, ErrorHandler);
#define LUA_LOADLIB(lib) \
lua_pushcfunction(L, luaopen_##lib); \
lua_pcall(L, 0, 0, 1);
// We only load three basic libraries, we don't want to expose dangerous OS functions
LUA_LOADLIB(base);
LUA_LOADLIB(table);
LUA_LOADLIB(math);
LUA_LOADLIB(string);
bool success = LoadResourceScript(L, "HEADER");
assert(success);
if (!success)
return false;
// Attempt to load the script, if it fails load the internal script instead
success = LoadFileScript(L, fn);
if (!success)
success = LoadResourceScript(L, "INPUT");
assert(success);
// If the lua script fails, we set it on the controllers
if (!success)
return false;
OculusTouch* touch = dynamic_cast<OculusTouch*>(device);
if (touch)
touch->m_Script = L;
}
}
return true;
}
void InputManager::GetTrackingState(ovrSession session, ovrTrackingState* outState, double absTime)
{
if (session->Details->UseHack(SessionDetails::HACK_WAIT_IN_TRACKING_STATE))
vr::VRCompositor()->WaitGetPoses(nullptr, 0, nullptr, 0);
// Calculate the relative prediction time
float relTime = 0.0f;
if (absTime > 0.0f)
relTime = float(absTime - ovr_GetTimeInSeconds());
if (relTime > 0.0f)
relTime += m_fVsyncToPhotons;
// Get the device poses
vr::ETrackingUniverseOrigin origin = session->TrackingOrigin;
vr::TrackedDevicePose_t poses[vr::k_unMaxTrackedDeviceCount];
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(origin, relTime, poses, vr::k_unMaxTrackedDeviceCount);
// Convert the head pose
outState->HeadPose = TrackedDevicePoseToOVRPose(poses[vr::k_unTrackedDeviceIndex_Hmd], m_LastPoses[vr::k_unTrackedDeviceIndex_Hmd], absTime);
outState->StatusFlags = TrackedDevicePoseToOVRStatusFlags(poses[vr::k_unTrackedDeviceIndex_Hmd]);
// Convert the hand poses
InputSettings* settings = session->Settings->Input;
vr::TrackedDeviceIndex_t hands[] = { vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand),
vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand) };
for (int i = 0; i < ovrHand_Count; i++)
{
vr::TrackedDeviceIndex_t deviceIndex = hands[i];
if (deviceIndex == vr::k_unTrackedDeviceIndexInvalid)
{
outState->HandPoses[i].ThePose = OVR::Posef::Identity();
continue;
}
vr::TrackedDevicePose_t pose;
vr::VRSystem()->ApplyTransform(&pose, &poses[deviceIndex], &settings->TouchOffset[i]);
outState->HandPoses[i] = TrackedDevicePoseToOVRPose(pose, m_LastPoses[deviceIndex], absTime);
outState->HandStatusFlags[i] = TrackedDevicePoseToOVRStatusFlags(poses[deviceIndex]);
}
if (origin == vr::TrackingUniverseSeated)
{
REV::Matrix4f origin = (REV::Matrix4f)vr::VRSystem()->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
// The calibrated origin should be the location of the seated origin relative to the absolute tracking space.
// It currently describes the location of the absolute origin relative to the seated origin, so we have to invert it.
origin.Invert();
outState->CalibratedOrigin.Orientation = OVR::Quatf(origin);
outState->CalibratedOrigin.Position = origin.GetTranslation();
}
else
{
// In a standing universe we don't calibrate the origin outside of the room setup, thus this should always be the
// identity matrix.
outState->CalibratedOrigin.Orientation = OVR::Quatf::Identity();
outState->CalibratedOrigin.Position = OVR::Vector3f();
}
}
ovrResult InputManager::GetDevicePoses(ovrTrackedDeviceType* deviceTypes, int deviceCount, double absTime, ovrPoseStatef* outDevicePoses)
{
// Get the device poses
vr::ETrackingUniverseOrigin space = vr::VRCompositor()->GetTrackingSpace();
float relTime = float(absTime - ovr_GetTimeInSeconds());
vr::TrackedDevicePose_t poses[vr::k_unMaxTrackedDeviceCount];
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(space, relTime, poses, vr::k_unMaxTrackedDeviceCount);
// Get the generic tracker indices
vr::TrackedDeviceIndex_t trackers[vr::k_unMaxTrackedDeviceCount];
vr::VRSystem()->GetSortedTrackedDeviceIndicesOfClass(vr::TrackedDeviceClass_GenericTracker, trackers, vr::k_unMaxTrackedDeviceCount);
for (int i = 0; i < deviceCount; i++)
{
// Get the index for device types we recognize
uint32_t index = vr::k_unTrackedDeviceIndexInvalid;
switch (deviceTypes[i])
{
case ovrTrackedDevice_HMD:
index = vr::k_unTrackedDeviceIndex_Hmd;
break;
case ovrTrackedDevice_LTouch:
index = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand);
break;
case ovrTrackedDevice_RTouch:
index = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand);
break;
case ovrTrackedDevice_Object0:
index = trackers[0];
break;
case ovrTrackedDevice_Object1:
index = trackers[1];
break;
case ovrTrackedDevice_Object2:
index = trackers[2];
break;
case ovrTrackedDevice_Object3:
index = trackers[3];
break;
}
// If the tracking index is invalid it will fall outside of the range of the array
if (index >= vr::k_unMaxTrackedDeviceCount)
return ovrError_DeviceUnavailable;
outDevicePoses[i] = TrackedDevicePoseToOVRPose(poses[index], m_LastPoses[index], absTime);
}
return ovrSuccess;
}
/* Controller child-classes */
void InputManager::OculusTouch::HapticsThread(OculusTouch* device)
{
std::chrono::microseconds freq(std::chrono::seconds(1));
freq /= REV_HAPTICS_SAMPLE_RATE;
while (device->m_bHapticsRunning)
{
vr::TrackedDeviceIndex_t touch = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(device->GetRole());
uint16_t duration = (uint16_t)((float)freq.count() * device->m_Haptics.GetSample());
if (duration > 0)
vr::VRSystem()->TriggerHapticPulse(touch, 0, duration);
std::this_thread::sleep_for(freq);
}
}
InputManager::OculusTouch::OculusTouch(vr::ETrackedControllerRole role)
: m_Script()
, m_Role(role)
, m_bHapticsRunning(true)
{
m_HapticsThread = std::thread(HapticsThread, this);
}
InputManager::OculusTouch::~OculusTouch()
{
m_bHapticsRunning = false;
m_HapticsThread.join();
}
ovrControllerType InputManager::OculusTouch::GetType()
{
return m_Role == vr::TrackedControllerRole_LeftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch;
}
bool InputManager::OculusTouch::IsConnected() const
{
// Check if the Vive controller is assigned
vr::TrackedDeviceIndex_t touch = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(m_Role);
return touch != vr::k_unTrackedDeviceIndexInvalid;
}
void InputManager::OculusTouch::AddStateField(lua_State* L, vr::TrackedDeviceIndex_t index,
vr::VRControllerState_t& state, vr::EVRButtonId button, const char* name)
{
bool isAxis = vr::k_EButton_Axis0 <= button && button <= vr::k_EButton_Axis4;
if (isAxis)
{
// We put axes in the array part
lua_pushnumber(L, button - vr::k_EButton_Axis0 + 1);
}
else
{
// And buttons in the hash table
if (!name)
2017-12-22 16:41:11 +01:00
name = s_ButtonNames[button];
lua_pushstring(L, name);
}
lua_createtable(L, 0, isAxis ? 5 : 2);
lua_pushboolean(L, !!(state.ulButtonPressed & vr::ButtonMaskFromId(button)));
lua_setfield(L, -2, "pressed");
lua_pushboolean(L, !!(state.ulButtonTouched & vr::ButtonMaskFromId(button)));
lua_setfield(L, -2, "touched");
if (isAxis)
{
int n = button - vr::k_EButton_Axis0;
vr::VRControllerAxis_t& axis = state.rAxis[n];
lua_pushnumber(L, axis.x);
lua_setfield(L, -2, "x");
lua_pushnumber(L, axis.y);
lua_setfield(L, -2, "y");
lua_pushstring(L, s_TypeNames[(m_AxisTypes >> n) & 3]);
lua_setfield(L, -2, "type");
}
lua_settable(L, -3);
}
void InputManager::OculusTouch::CreateStateTable(lua_State* L, vr::TrackedDeviceIndex_t index, vr::VRControllerState_t& state)
2017-12-18 19:03:52 +01:00
{
lua_createtable(L, vr::k_unControllerStateAxisCount, 12);
// Known buttons
for (int i = vr::k_EButton_System; i <= vr::k_EButton_A; i++)
AddStateField(L, index, state, (vr::EVRButtonId)i);
// Known axes
for (int i = vr::k_EButton_Axis0; i <= vr::k_EButton_Axis4; i++)
AddStateField(L, index, state, (vr::EVRButtonId)i);
// Some controllers define additional undocumented buttons
AddStateField(L, index, state, (vr::EVRButtonId)8, "B");
AddStateField(L, index, state, (vr::EVRButtonId)9, "X");
AddStateField(L, index, state, (vr::EVRButtonId)10, "Y");
// ... are you really going to use the headset sensor for input?
AddStateField(L, index, state, vr::k_EButton_ProximitySensor);
2017-12-18 19:03:52 +01:00
}
// TODO: Make GetInputState reentrant (thread-safe).
bool InputManager::OculusTouch::GetInputState(ovrSession session, ovrInputState* inputState)
{
// Get controller index
vr::TrackedDeviceIndex_t index = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(m_Role);
ovrHandType hand = (m_Role == vr::TrackedControllerRole_LeftHand) ? ovrHand_Left : ovrHand_Right;
InputSettings* settings = session->Settings->Input;
lua_State* L = m_Script.load();
if (!L || index == vr::k_unTrackedDeviceIndexInvalid)
return false;
vr::VRControllerState_t state;
vr::VRSystem()->GetControllerState(index, &state, sizeof(state));
CreateStateTable(L, index, state);
lua_setglobal(L, "state");
lua_pushnumber(L, inputState->TimeInSeconds);
lua_setglobal(L, "time");
2017-12-21 18:17:58 +01:00
uint32_t size = vr::VRSystem()->GetStringTrackedDeviceProperty(index, vr::Prop_ModelNumber_String, nullptr, 0);
std::vector<char> model(size);
vr::VRSystem()->GetStringTrackedDeviceProperty(index, vr::Prop_ModelNumber_String, model.data(), size);
lua_pushstring(L, model.data());
lua_setglobal(L, "controller_model");
lua_getglobal(L, "GetButtons");
lua_pushboolean(L, hand == ovrHand_Right);
if (lua_pcall(L, 1, LUA_MULTRET, 1))
return false;
while (lua_gettop(L) > 1)
{
inputState->Buttons |= lua_tointeger(L, -1);
lua_pop(L, 1);
}
lua_getglobal(L, "GetTouches");
lua_pushboolean(L, hand == ovrHand_Right);
if (lua_pcall(L, 1, LUA_MULTRET, 1))
return false;
while (lua_gettop(L) > 1)
{
inputState->Touches |= lua_tointeger(L, -1);
lua_pop(L, 1);
}
lua_getglobal(L, "GetTriggers");
lua_pushboolean(L, hand == ovrHand_Right);
lua_createtable(L, 0, 3);
lua_pushboolean(L, settings->ToggleGrip == revGrip_Normal);
lua_setfield(L, -2, "normal");
lua_pushboolean(L, settings->ToggleGrip == revGrip_Toggle);
lua_setfield(L, -2, "toggle");
lua_pushboolean(L, settings->ToggleGrip == revGrip_Hybrid);
lua_setfield(L, -2, "hybrid");
lua_pushnumber(L, settings->ToggleDelay);
lua_setfield(L, -2, "delay");
2017-12-18 19:03:52 +01:00
lua_pushboolean(L, settings->TriggerAsGrip);
lua_setfield(L, -2, "trigger");
if (lua_pcall(L, 2, 2, 1))
return false;
inputState->IndexTrigger[hand] = (float)lua_tonumber(L, -2);
inputState->HandTrigger[hand] = (float)lua_tonumber(L, -1);
lua_pop(L, 2);
lua_getglobal(L, "GetThumbstick");
lua_pushboolean(L, hand == ovrHand_Right);
lua_pushnumber(L, settings->Deadzone);
if (lua_pcall(L, 2, 2, 1))
return false;
inputState->Thumbstick[hand].x = (float)lua_tonumber(L, -2);
inputState->Thumbstick[hand].y = (float)lua_tonumber(L, -1);
lua_pop(L, 2);
lua_getglobal(L, "GetThumbstick");
lua_pushboolean(L, hand == ovrHand_Right);
lua_pushnumber(L, 0);
if (lua_pcall(L, 2, 2, 1))
return false;
2017-12-13 16:48:52 +01:00
inputState->ThumbstickNoDeadzone[hand].x = (float)lua_tonumber(L, -2);
inputState->ThumbstickNoDeadzone[hand].y = (float)lua_tonumber(L, -1);
lua_pop(L, 2);
// We don't apply deadzones yet on triggers and grips
inputState->IndexTriggerNoDeadzone[hand] = inputState->IndexTrigger[hand];
inputState->HandTriggerNoDeadzone[hand] = inputState->HandTrigger[hand];
// We have no way to get raw values
inputState->ThumbstickRaw[hand] = inputState->ThumbstickNoDeadzone[hand];
inputState->IndexTriggerRaw[hand] = inputState->IndexTriggerNoDeadzone[hand];
inputState->HandTriggerRaw[hand] = inputState->HandTriggerNoDeadzone[hand];
2017-12-18 19:03:52 +01:00
lua_getglobal(L, "state");
lua_setglobal(L, "last_state");
return true;
}
bool InputManager::OculusRemote::IsConnected() const
{
// Check if a Vive controller is available
uint32_t controllerCount = vr::VRSystem()->GetSortedTrackedDeviceIndicesOfClass(vr::TrackedDeviceClass_Controller, nullptr, 0);
// If only one controller is available, the Oculus Remote is connected
return controllerCount == 1;
}
bool InputManager::OculusRemote::GetInputState(ovrSession session, ovrInputState* inputState)
{
// Get controller indices.
vr::TrackedDeviceIndex_t remote;
vr::VRSystem()->GetSortedTrackedDeviceIndicesOfClass(vr::TrackedDeviceClass_Controller, &remote, 1);
if (remote == vr::k_unTrackedDeviceIndexInvalid)
return false;
vr::VRControllerState_t state;
2016-12-03 04:15:21 +01:00
vr::VRSystem()->GetControllerState(remote, &state, sizeof(state));
if (state.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_ApplicationMenu))
inputState->Buttons |= ovrButton_Back;
if (state.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Touchpad))
{
vr::VRControllerAxis_t axis = state.rAxis[0];
float magnitude = sqrt(axis.x*axis.x + axis.y*axis.y);
if (magnitude < 0.5f)
{
inputState->Buttons |= ovrButton_Enter;
}
else
{
if (axis.y < axis.x) {
if (axis.y < -axis.x)
inputState->Buttons |= ovrButton_Down;
else
inputState->Buttons |= ovrButton_Right;
}
else {
if (axis.y < -axis.x)
inputState->Buttons |= ovrButton_Left;
else
inputState->Buttons |= ovrButton_Up;
}
}
}
return state.ulButtonPressed != 0;
}
InputManager::XboxGamepad::XboxGamepad()
{
m_XInput = LoadLibrary(L"xinput1_3.dll");
if (m_XInput)
{
GetState = (_XInputGetState)GetProcAddress(m_XInput, "XInputGetState");
SetState = (_XInputSetState)GetProcAddress(m_XInput, "XInputSetState");
}
}
InputManager::XboxGamepad::~XboxGamepad()
{
FreeLibrary(m_XInput);
}
bool InputManager::XboxGamepad::IsConnected() const
{
if (!m_XInput)
return false;
// Check for Xbox controller
XINPUT_STATE input;
return GetState(0, &input) == ERROR_SUCCESS;
}
bool InputManager::XboxGamepad::GetInputState(ovrSession session, ovrInputState* inputState)
{
if (!m_XInput)
return false;
// Use XInput for Xbox controllers.
XINPUT_STATE state;
if (GetState(0, &state) == ERROR_SUCCESS)
{
// Convert the buttons
bool active = false;
WORD buttons = state.Gamepad.wButtons;
if (buttons & XINPUT_GAMEPAD_DPAD_UP)
inputState->Buttons |= ovrButton_Up;
if (buttons & XINPUT_GAMEPAD_DPAD_DOWN)
inputState->Buttons |= ovrButton_Down;
if (buttons & XINPUT_GAMEPAD_DPAD_LEFT)
inputState->Buttons |= ovrButton_Left;
if (buttons & XINPUT_GAMEPAD_DPAD_RIGHT)
inputState->Buttons |= ovrButton_Right;
if (buttons & XINPUT_GAMEPAD_START)
inputState->Buttons |= ovrButton_Enter;
if (buttons & XINPUT_GAMEPAD_BACK)
inputState->Buttons |= ovrButton_Back;
if (buttons & XINPUT_GAMEPAD_LEFT_THUMB)
inputState->Buttons |= ovrButton_LThumb;
if (buttons & XINPUT_GAMEPAD_RIGHT_THUMB)
inputState->Buttons |= ovrButton_RThumb;
if (buttons & XINPUT_GAMEPAD_LEFT_SHOULDER)
inputState->Buttons |= ovrButton_LShoulder;
if (buttons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
inputState->Buttons |= ovrButton_RShoulder;
if (buttons & XINPUT_GAMEPAD_A)
inputState->Buttons |= ovrButton_A;
if (buttons & XINPUT_GAMEPAD_B)
inputState->Buttons |= ovrButton_B;
if (buttons & XINPUT_GAMEPAD_X)
inputState->Buttons |= ovrButton_X;
if (buttons & XINPUT_GAMEPAD_Y)
inputState->Buttons |= ovrButton_Y;
active = (buttons != 0);
// Convert the axes
SHORT deadzones[] = { XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE };
for (int i = 0; i < ovrHand_Count; i++)
{
float X, Y, trigger;
if (i == ovrHand_Left)
{
X = state.Gamepad.sThumbLX;
Y = state.Gamepad.sThumbLY;
trigger = state.Gamepad.bLeftTrigger;
}
if (i == ovrHand_Right)
{
X = state.Gamepad.sThumbRX;
Y = state.Gamepad.sThumbRY;
trigger = state.Gamepad.bRightTrigger;
}
//determine how far the controller is pushed
float magnitude = sqrt(X*X + Y*Y);
//determine the direction the controller is pushed
float normalizedX = X / magnitude;
float normalizedY = Y / magnitude;
inputState->ThumbstickNoDeadzone[i].x = normalizedX;
inputState->ThumbstickNoDeadzone[i].y = normalizedY;
//check if the controller is outside a circular dead zone
if (magnitude > deadzones[i])
{
//clip the magnitude at its expected maximum value
if (magnitude > 32767) magnitude = 32767;
//adjust magnitude relative to the end of the dead zone
magnitude -= deadzones[i];
//optionally normalize the magnitude with respect to its expected range
//giving a magnitude value of 0.0 to 1.0
float normalizedMagnitude = magnitude / (32767 - deadzones[i]);
inputState->Thumbstick[i].x = normalizedMagnitude * normalizedX;
inputState->Thumbstick[i].y = normalizedMagnitude * normalizedY;
active = true;
}
if (trigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
{
//clip the magnitude at its expected maximum value
if (trigger > 255) trigger = 255;
inputState->IndexTriggerNoDeadzone[i] = trigger / 255.0f;
//adjust magnitude relative to the end of the dead zone
trigger -= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
//optionally normalize the magnitude with respect to its expected range
//giving a magnitude value of 0.0 to 1.0
float normalizedTrigger = trigger / (255 - XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
inputState->IndexTrigger[i] = normalizedTrigger;
active = true;
}
}
return active;
}
return false;
}
void InputManager::XboxGamepad::SetVibration(float frequency, float amplitude)
{
if (!m_XInput)
return;
// TODO: Disable the rumbler after a nominal amount of time
XINPUT_VIBRATION vibration;
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
if (frequency > 0.0f)
{
// The right motor is the high-frequency motor, the left motor is the low-frequency motor.
if (frequency > 0.5f)
vibration.wRightMotorSpeed = WORD(65535.0f * amplitude);
else
vibration.wLeftMotorSpeed = WORD(65535.0f * amplitude);
}
SetState(0, &vibration);
}