#ifndef VR_INPUT_HPP #define VR_INPUT_HPP #include "vrviewer.hpp" #include "openxraction.hpp" #include "../mwinput/inputmanagerimp.hpp" namespace MWVR { // The OpenMW input manager iterates from 0 to A_Last in its actions enum. // I don't know that it would cause any ill effects, but i nonetheless do not // want to contaminate the input manager with my OpenXR specific actions. // Therefore i add them here to a separate enum whose values start past A_Last. enum VrActions { A_VrFirst = MWInput::A_Last + 1, A_ActivateTouch, A_HapticsLeft, A_HapticsRight, A_HandPoseLeft, A_HandPoseRight, A_MenuUpDown, A_MenuLeftRight, A_MenuSelect, A_MenuBack, A_Recenter, A_VrLast }; enum class ActionPath { Pose = 0, Haptic, Menu, ThumbstickX, ThumbstickY, ThumbstickClick, Select, Squeeze, Trigger, X, Y, A, B, Last }; //class OpenXRAction; //! Action for applying haptics class HapticsAction { public: HapticsAction(std::unique_ptr xrAction) : mXRAction{ std::move(xrAction) } {}; //! Apply vibration at the given amplitude void apply(float amplitude); //! Convenience operator XrAction() { return *mXRAction; } private: std::unique_ptr mXRAction; float mAmplitude{ 0.f }; }; //! Action for capturing tracking information class PoseAction { public: PoseAction(std::unique_ptr xrAction); //! Current value of an axis or lever action Pose value() const { return mValue; } //! Previous value Pose previousValue() const { return mPrevious; } //! Convenience operator XrAction() { return *mXRAction; } //! Update pose value void update(long long time); private: std::unique_ptr mXRAction; XrSpace mXRSpace; Pose mValue{}; Pose mPrevious{}; }; //! Generic action class Action { public: Action(int openMWAction, std::unique_ptr xrAction) : mXRAction(std::move(xrAction)), mOpenMWAction(openMWAction) {} virtual ~Action() {}; //! True if action changed to being released in the last update bool isActive() const { return mActive; }; //! True if activation turned on this update (i.e. onPress) bool onActivate() const { return mOnActivate; } //! True if activation turned off this update (i.e. onRelease) bool onDeactivate() const { return mOnDeactivate; } //! OpenMW Action code of this action int openMWActionCode() const { return mOpenMWAction; } //! Current value of an axis or lever action float value() const { return mValue; } //! Previous value float previousValue() const { return mPrevious; } //! Update internal states. Note that subclasses maintain both mValue and mActivate to allow //! axis and press to subtitute one another. virtual void update() = 0; //! Determines if an action update should be queued virtual bool shouldQueue() const = 0; //! Convenience operator XrAction() { return *mXRAction; } //! Update and queue action if applicable void updateAndQueue(std::deque& queue); protected: std::unique_ptr mXRAction; int mOpenMWAction; float mValue{ 0.f }; float mPrevious{ 0.f }; bool mActive{ false }; bool mOnActivate{ false }; bool mOnDeactivate{ false }; }; //! Convenience using ActionPtr = std::unique_ptr; //! Action that activates once on release. //! Times out if the button is held longer than gHoldDelay. class ButtonPressAction : public Action { public: using Action::Action; static const XrActionType ActionType = XR_ACTION_TYPE_BOOLEAN_INPUT; void update() override; virtual bool shouldQueue() const override { return onActivate() || onDeactivate(); } bool mPressed{ false }; std::chrono::steady_clock::time_point mPressTime{}; std::chrono::steady_clock::time_point mTimeout{}; }; //! Action that activates once on a long press //! The press time is the same as the timeout for a regular press, allowing keys with double roles. class ButtonLongPressAction : public Action { public: using Action::Action; static const XrActionType ActionType = XR_ACTION_TYPE_BOOLEAN_INPUT; void update() override; virtual bool shouldQueue() const override { return onActivate() || onDeactivate(); } bool mPressed{ false }; bool mActivated{ false }; std::chrono::steady_clock::time_point mPressTime{}; std::chrono::steady_clock::time_point mTimein{}; }; //! Action that is active whenever the button is pressed down. //! Useful for e.g. non-toggling sneak and automatically repeating actions class ButtonHoldAction : public Action { public: using Action::Action; static const XrActionType ActionType = XR_ACTION_TYPE_BOOLEAN_INPUT; void update() override; virtual bool shouldQueue() const override { return mActive || onDeactivate(); } bool mPressed{ false }; }; //! Action for axis actions, such as thumbstick axes or certain triggers/squeeze levers. //! Float axis are considered active whenever their magnitude is greater than gAxisEpsilon. This is useful //! as a touch subtitute on levers without touch. class AxisAction : public Action { public: using Action::Action; static const XrActionType ActionType = XR_ACTION_TYPE_FLOAT_INPUT; void update() override; virtual bool shouldQueue() const override { return mActive || onDeactivate(); } }; } #endif