You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
5.1 KiB
C++
175 lines
5.1 KiB
C++
#include "vrinput.hpp"
|
|
#include "openxrdebug.hpp"
|
|
#include "vrenvironment.hpp"
|
|
#include "openxrmanagerimpl.hpp"
|
|
#include "openxrtypeconversions.hpp"
|
|
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <array>
|
|
#include <iostream>
|
|
#include <cassert>
|
|
|
|
namespace MWVR
|
|
{
|
|
|
|
//! Delay before a long-press action is activated (and regular press is discarded)
|
|
//! TODO: Make this configurable?
|
|
static std::chrono::milliseconds gActionTime{ 666 };
|
|
//! Magnitude above which an axis action is considered active
|
|
static float gAxisEpsilon{ 0.01f };
|
|
|
|
void HapticsAction::apply(float amplitude)
|
|
{
|
|
mAmplitude = std::max(0.f, std::min(1.f, amplitude));
|
|
mXRAction->applyHaptics(XR_NULL_PATH, mAmplitude);
|
|
}
|
|
|
|
PoseAction::PoseAction(std::unique_ptr<OpenXRAction> xrAction)
|
|
: mXRAction(std::move(xrAction))
|
|
, mXRSpace{ XR_NULL_HANDLE }
|
|
{
|
|
auto* xr = Environment::get().getManager();
|
|
XrActionSpaceCreateInfo createInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
|
|
createInfo.action = *mXRAction;
|
|
createInfo.poseInActionSpace.orientation.w = 1.f;
|
|
createInfo.subactionPath = XR_NULL_PATH;
|
|
CHECK_XRCMD(xrCreateActionSpace(xr->impl().xrSession(), &createInfo, &mXRSpace));
|
|
VrDebug::setName(mXRSpace, "OpenMW XR Action Space " + mXRAction->mName);
|
|
}
|
|
|
|
void PoseAction::update(long long time)
|
|
{
|
|
mPrevious = mValue;
|
|
|
|
auto* xr = Environment::get().getManager();
|
|
XrSpace referenceSpace = xr->impl().getReferenceSpace(ReferenceSpace::STAGE);
|
|
|
|
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
|
|
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };
|
|
location.next = &velocity;
|
|
CHECK_XRCMD(xrLocateSpace(mXRSpace, referenceSpace, time, &location));
|
|
if (!(location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT))
|
|
// Quat must have a magnitude of 1 but openxr sets it to 0 when tracking is unavailable.
|
|
// I want a no-track pose to still be a valid quat so osg won't throw errors
|
|
location.pose.orientation.w = 1;
|
|
|
|
mValue = Pose{
|
|
fromXR(location.pose.position),
|
|
fromXR(location.pose.orientation)
|
|
};
|
|
}
|
|
|
|
void Action::updateAndQueue(std::deque<const Action*>& queue)
|
|
{
|
|
bool old = mActive;
|
|
mPrevious = mValue;
|
|
update();
|
|
bool changed = old != mActive;
|
|
mOnActivate = changed && mActive;
|
|
mOnDeactivate = changed && !mActive;
|
|
|
|
if (shouldQueue())
|
|
{
|
|
queue.push_back(this);
|
|
}
|
|
}
|
|
|
|
void ButtonPressAction::update()
|
|
{
|
|
mActive = false;
|
|
bool old = mPressed;
|
|
mXRAction->getBool(0, mPressed);
|
|
bool changed = old != mPressed;
|
|
if (changed && mPressed)
|
|
{
|
|
mPressTime = std::chrono::steady_clock::now();
|
|
mTimeout = mPressTime + gActionTime;
|
|
}
|
|
if (changed && !mPressed)
|
|
{
|
|
if (std::chrono::steady_clock::now() < mTimeout)
|
|
{
|
|
mActive = true;
|
|
}
|
|
}
|
|
|
|
mValue = mPressed ? 1.f : 0.f;
|
|
}
|
|
|
|
void ButtonLongPressAction::update()
|
|
{
|
|
mActive = false;
|
|
bool old = mPressed;
|
|
mXRAction->getBool(0, mPressed);
|
|
bool changed = old != mPressed;
|
|
if (changed && mPressed)
|
|
{
|
|
mPressTime = std::chrono::steady_clock::now();
|
|
mTimein = mPressTime + gActionTime;
|
|
mActivated = false;
|
|
}
|
|
if (mPressed && !mActivated)
|
|
{
|
|
if (std::chrono::steady_clock::now() >= mTimein)
|
|
{
|
|
mActive = mActivated = true;
|
|
mValue = 1.f;
|
|
}
|
|
}
|
|
if (changed && !mPressed)
|
|
{
|
|
mValue = 0.f;
|
|
}
|
|
}
|
|
|
|
|
|
void ButtonHoldAction::update()
|
|
{
|
|
mXRAction->getBool(0, mPressed);
|
|
mActive = mPressed;
|
|
|
|
mValue = mPressed ? 1.f : 0.f;
|
|
}
|
|
|
|
|
|
AxisAction::AxisAction(int openMWAction, std::unique_ptr<OpenXRAction> xrAction, std::shared_ptr<AxisAction::Deadzone> deadzone)
|
|
: Action(openMWAction, std::move(xrAction))
|
|
, mDeadzone(deadzone)
|
|
{
|
|
}
|
|
|
|
void AxisAction::update()
|
|
{
|
|
mActive = false;
|
|
mXRAction->getFloat(0, mValue);
|
|
mDeadzone->applyDeadzone(mValue);
|
|
|
|
if (std::fabs(mValue) > gAxisEpsilon)
|
|
mActive = true;
|
|
else
|
|
mValue = 0.f;
|
|
}
|
|
|
|
void AxisAction::Deadzone::applyDeadzone(float& value)
|
|
{
|
|
float sign = std::copysignf(1.f, value);
|
|
float magnitude = std::fabs(value);
|
|
magnitude = std::min(mActiveRadiusOuter, magnitude);
|
|
magnitude = std::max(0.f, magnitude - mActiveRadiusInner);
|
|
value = sign * magnitude * mActiveScale;
|
|
|
|
}
|
|
|
|
void AxisAction::Deadzone::setDeadzoneRadius(float deadzoneRadius)
|
|
{
|
|
deadzoneRadius = std::min(std::max(deadzoneRadius, 0.0f), 0.5f - 1e-5f);
|
|
mActiveRadiusInner = deadzoneRadius;
|
|
mActiveRadiusOuter = 1.f - deadzoneRadius;
|
|
float activeRadius = mActiveRadiusOuter - mActiveRadiusInner;
|
|
assert(activeRadius > 0.f);
|
|
mActiveScale = 1.f / activeRadius;
|
|
}
|
|
|
|
}
|