// SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2021 James Hogan #ifndef OSGXR_OPENXR_ACTION #define OSGXR_OPENXR_ACTION 1 #include "ActionSet.h" #include "Path.h" #include #include #include #include #include namespace osgXR { namespace OpenXR { class Path; class Space; class Action : public osg::Referenced { public: Action(ActionSet *actionSet, const std::string &name, const std::string &localizedName, XrActionType type); virtual ~Action(); // Action initialisation void addSubaction(const Path &path); // Returns true on success bool init(); // Error checking inline bool valid() const { return _action != XR_NULL_HANDLE; } inline bool check(XrResult result, const char *warnMsg) const { return _actionSet->check(result, warnMsg); } // Conversions inline const osg::ref_ptr getActionSet() const { return _actionSet; } inline const osg::ref_ptr getInstance() const { return _actionSet->getInstance(); } inline XrInstance getXrInstance() const { return _actionSet->getXrInstance(); } inline XrActionSet getXrActionSet() const { return _actionSet->getXrActionSet(); } inline XrAction getXrAction() const { return _action; } protected: // Action data osg::ref_ptr _actionSet; std::vector _subactionPaths; XrActionCreateInfo _createInfo; XrAction _action; }; /// Base action state for inputs. class ActionStateBase : public osg::Referenced { public: // Constructors ActionStateBase(Action *action, Session *session, Path subactionPath = Path()); virtual ~ActionStateBase(); // Error checking inline bool valid() const { return _valid; } inline bool check(XrResult result, const char *warnMsg) const { return _action->check(result, warnMsg); } protected: // Utilities for synchronisation // Find whether the state needs update and update sync counter bool checkUpdate(); // Member data osg::ref_ptr _action; osg::ref_ptr _session; Path _subactionPath; bool _valid; unsigned int _syncCount; }; /// All action states have an isActive field. template class ActionStateCommon : public ActionStateBase { private: typedef ActionStateBase Base; public: // Constructors ActionStateCommon(Action *action, Session *session, Path subactionPath = Path()) : Base(action, session, subactionPath) { } // Accessors bool isActive() const { assert(valid()); return _state.isActive; } // Operations /// Update state if a sync has taken place bool update() { if (Base::checkUpdate()) return updateState(); return valid(); } protected: // Protected operations bool updateState(); // Data members T _state; }; // These are the base action state classes typedef ActionStateCommon ActionStateCommonBoolean; typedef ActionStateCommon ActionStateCommonFloat; typedef ActionStateCommon ActionStateCommonVector2f; typedef ActionStateCommon ActionStateCommonPose; // Convert action values to app / OSG friendly formats template struct ActionTypeInfo; // XrBool32 -> bool template <> struct ActionTypeInfo { static bool convert(XrBool32 value) { return value; } static bool defaultValue() { return false; } }; // float -> float template <> struct ActionTypeInfo { static float convert(float value) { return value; } static float defaultValue() { return 0.0f; } }; // XrVector2f -> osg::Vec2f template <> struct ActionTypeInfo { static osg::Vec2f convert(const XrVector2f &value) { return osg::Vec2f(value.x, value.y); } static osg::Vec2f defaultValue() { return osg::Vec2f(0.0f, 0.0f); } }; /// Some action states have currentValue and related fields. template class ActionStateSimple : public ActionStateCommon { private: typedef ActionStateCommon Base; public: typedef ActionTypeInfo Info; // Constructors ActionStateSimple(Action *action, Session *session, Path subactionPath = Path()) : Base(action, session, subactionPath) { } // Accessors auto getCurrentState() const { assert(this->valid()); return Info::convert(Base::_state.currentState); } bool hasChangedSinceLastSync() const { assert(this->valid()); return Base::_state.changedSinceLastSync; } XrTime getLastChangedTime() const { assert(this->valid()); return Base::_state.lastChangedTime; } }; // These are the simple action state classes typedef ActionStateSimple ActionStateBoolean; typedef ActionStateSimple ActionStateFloat; typedef ActionStateSimple ActionStateVector2f; /// Specialise Action for a specific input type template class ActionTyped : public Action { public: typedef T State; ActionTyped(ActionSet *actionSet, const std::string &name, const std::string &localizedName) : Action(actionSet, name, localizedName, type) { } osg::ref_ptr createState(Session *session, Path subactionPath = Path()) { return new State(this, session, subactionPath); } }; // So ActionStatePose etc can take ActionPose etc in constructor class ActionStatePose; class ActionStateVibration; // These are the final typed action classes typedef ActionTyped ActionBoolean; typedef ActionTyped ActionFloat; typedef ActionTyped ActionVector2f; typedef ActionTyped ActionPose; typedef ActionTyped ActionVibration; /// Pose actions have their own way to get the pose class ActionStatePose : public ActionStateCommon { private: typedef ActionStateCommon Base; public: // Constructors ActionStatePose(ActionPose *action, Session *session, Path subactionPath = Path()); ~ActionStatePose(); // Accessors Space *getSpace() { return _space.get(); } protected: osg::ref_ptr _space; }; class ActionStateVibration : public osg::Referenced { public: // Constructors ActionStateVibration(ActionVibration *action, Session *session, Path subactionPath = Path()); // Error checking inline bool check(XrResult result, const char *warnMsg) const { return _action->check(result, warnMsg); } // Haptic vibrations bool applyHapticFeedback(int64_t duration_ns, float frequency, float amplitude) const; bool stopHapticFeedback() const; protected: // Member data osg::ref_ptr _action; osg::ref_ptr _session; Path _subactionPath; }; } // osgXR::OpenXR } // osgXR #endif