#ifndef VR_INPUT_HPP #define VR_INPUT_HPP #include "vrviewer.hpp" #include "openxraction.hpp" #include "../mwinput/inputmanagerimp.hpp" namespace MWVR { /// Extension of MWInput's set of actions. enum VrActions { A_VrFirst = MWInput::A_Last + 1, A_VrMetaMenu, A_ActivateTouch, A_HapticsLeft, A_HapticsRight, A_HandPoseLeft, A_HandPoseRight, A_MenuUpDown, A_MenuLeftRight, A_MenuSelect, A_MenuBack, A_Recenter, A_VrLast }; /// \brief Suggest a binding by binding an action to a path on a given hand (left or right). struct SuggestedBinding { int action; std::string path; }; using SuggestedBindings = std::vector; /// \brief Enumeration of action sets enum class ActionSet { GUI = 0, Gameplay = 1, }; /// \brief 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 }; }; /// \brief 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{}; }; /// \brief Generic action /// \sa ButtonPressAction ButtonLongPressAction ButtonHoldAction AxisAction 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 }; }; //! 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