// SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2021 James Hogan #include "Action.h" #include "ActionSet.h" #include "OpenXR/Action.h" #include "OpenXR/Session.h" #include "OpenXR/Space.h" #include using namespace osgXR; // Internal API Action::Private::Private(ActionSet *actionSet) : _actionSet(actionSet), _updated(true) { ActionSet::Private::get(_actionSet)->registerAction(this); } Action::Private::~Private() { ActionSet::Private::get(_actionSet)->unregisterAction(this); } void Action::Private::setName(const std::string &name) { _updated = true; _name = name; } const std::string &Action::Private::getName() const { return _name; } void Action::Private::setLocalizedName(const std::string &localizedName) { _updated = true; _localizedName = localizedName; } const std::string &Action::Private::getLocalizedName() const { return _localizedName; } void Action::Private::addSubaction(std::shared_ptr subaction) { _updated = true; _subactions.insert(subaction); } void Action::Private::cleanupInstance() { _updated = true; _action = nullptr; } void Action::Private::getBoundSources(std::vector &sourcePaths) const { OpenXR::Session *session = ActionSet::Private::get(_actionSet)->getSession(); if (_action.valid() && session) { std::vector paths; if (session->getActionBoundSources(_action, paths)) { // Convert XrPath's into std::string's OpenXR::Instance *instance = session->getInstance(); sourcePaths.resize(paths.size()); for (unsigned int i = 0; i < paths.size(); ++i) sourcePaths[i] = OpenXR::Path(instance, paths[i]).toString(); // Success! return; } } // Failure, clear output sourcePaths.resize(0); } void Action::Private::getBoundSourcesLocalizedNames(XrInputSourceLocalizedNameFlags whichComponents, std::vector &names) const { OpenXR::Session *session = ActionSet::Private::get(_actionSet)->getSession(); if (_action.valid() && session) { std::vector paths; if (session->getActionBoundSources(_action, paths)) { // Convert XrPath's into localized names names.resize(paths.size()); for (unsigned int i = 0; i < paths.size(); ++i) names[i] = session->getInputSourceLocalizedName(paths[i], whichComponents); // Success! return; } } // Failure, clear output names.resize(0); } namespace osgXR { template class ActionPrivateCommon : public Action::Private { public: typedef typename T::State State; ActionPrivateCommon(ActionSet *actionSet) : Private(actionSet) { } void cleanupSession() override { _states.clear(); } OpenXR::Action *setup(OpenXR::Instance *instance) override { OpenXR::ActionSet *actionSet = ActionSet::Private::get(_actionSet)->setup(instance); if (!actionSet) { // Can't continue without an action set _action = nullptr; _updated = true; } else if (_updated || actionSet != _action->getActionSet()) { _action = new T(actionSet, _name, _localizedName); for (auto &subaction: _subactions) _action->addSubaction(subaction->setup(instance)); _updated = false; } return _action; } State *getState(Subaction::Private *subaction = nullptr) { auto it = _states.find(subaction); if (it != _states.end()) return (*it).second.get(); OpenXR::Session *session = ActionSet::Private::get(_actionSet)->getSession(); if (session) { OpenXR::Path subactionPath; if (subaction) subactionPath = subaction->setup(session->getInstance()); OpenXR::Action *action = setup(session->getInstance()); if (action) { osg::ref_ptr ret = static_cast(_action.get())->createState(session, subactionPath); _states[subaction] = ret; return ret.get(); } } return nullptr; } protected: std::map> _states; }; template class ActionPrivateSimple : public ActionPrivateCommon { public: typedef typename T::State State; ActionPrivateSimple(ActionSet *actionSet) : ActionPrivateCommon(actionSet) { } auto getValue(Subaction::Private *subaction) { State *state = this->getState(subaction); if (state && state->update() && state->isActive()) return state->getCurrentState(); else return T::State::Info::defaultValue(); } }; typedef ActionPrivateSimple ActionPrivateBoolean; typedef ActionPrivateSimple ActionPrivateFloat; typedef ActionPrivateSimple ActionPrivateVector2f; class ActionPrivatePose : public ActionPrivateCommon { public: typedef OpenXR::ActionPose::State State; ActionPrivatePose(ActionSet *actionSet) : ActionPrivateCommon(actionSet) { } OpenXR::Space *getSpace(Subaction::Private *subaction) { State *state = getState(subaction); if (state && state->update() && state->isActive()) return state->getSpace(); else return nullptr; } bool locate(Subaction::Private *subaction, ActionPose::Location &location) { OpenXR::Space *space = getSpace(subaction); OpenXR::Session *session = ActionSet::Private::get(_actionSet)->getSession(); if (session && space) { OpenXR::Space::Location loc; bool ret = space->locate(session->getLocalSpace(), session->getLastDisplayTime(), loc); location = ActionPose::Location((ActionPose::Location::Flags)loc.getFlags(), loc.getOrientation(), loc.getPosition()); return ret; } else { location = ActionPose::Location(); return false; } } }; class ActionPrivateVibration : public ActionPrivateCommon { public: typedef OpenXR::ActionVibration::State State; ActionPrivateVibration(ActionSet *actionSet) : ActionPrivateCommon(actionSet) { } bool applyHapticFeedback(Subaction::Private *subaction, int64_t duration_ns, float frequency, float amplitude) { State *state = getState(subaction); if (!state) return false; return state->applyHapticFeedback(duration_ns, frequency, amplitude); } bool stopHapticFeedback(Subaction::Private *subaction) { State *state = getState(subaction); if (!state) return false; return state->stopHapticFeedback(); } }; } // Public API Action::Action(Private *priv) : _private(priv) { } Action::~Action() { } void Action::addSubaction(Subaction *subaction) { _private->addSubaction(Subaction::Private::get(subaction)); } void Action::setName(const std::string &name, const std::string &localizedName) { _private->setName(name); _private->setLocalizedName(localizedName); } void Action::setName(const std::string &name) { _private->setName(name); } const std::string &Action::getName() const { return _private->getName(); } void Action::setLocalizedName(const std::string &localizedName) { _private->setLocalizedName(localizedName); } const std::string &Action::getLocalizedName() const { return _private->getLocalizedName(); } void Action::getBoundSources(std::vector &sourcePaths) const { _private->getBoundSources(sourcePaths); } void Action::getBoundSourcesLocalizedNames(uint32_t whichComponents, std::vector &names) const { _private->getBoundSourcesLocalizedNames(whichComponents, names); } // ActionBoolean ActionBoolean::ActionBoolean(ActionSet *actionSet) : Action(new ActionPrivateBoolean(actionSet)) { } ActionBoolean::ActionBoolean(ActionSet *actionSet, const std::string &name) : Action(new ActionPrivateBoolean(actionSet)) { setName(name, name); } ActionBoolean::ActionBoolean(ActionSet *actionSet, const std::string &name, const std::string &localizedName) : Action(new ActionPrivateBoolean(actionSet)) { setName(name, localizedName); } bool ActionBoolean::getValue(Subaction *subaction) { auto privSubaction = Subaction::Private::get(subaction); return static_cast(Private::get(this))->getValue(privSubaction.get()); } // ActionFloat ActionFloat::ActionFloat(ActionSet *actionSet) : Action(new ActionPrivateFloat(actionSet)) { } ActionFloat::ActionFloat(ActionSet *actionSet, const std::string &name) : Action(new ActionPrivateFloat(actionSet)) { setName(name, name); } ActionFloat::ActionFloat(ActionSet *actionSet, const std::string &name, const std::string &localizedName) : Action(new ActionPrivateFloat(actionSet)) { setName(name, localizedName); } float ActionFloat::getValue(Subaction *subaction) { auto privSubaction = Subaction::Private::get(subaction); return static_cast(Private::get(this))->getValue(privSubaction.get()); } // ActionVector2f ActionVector2f::ActionVector2f(ActionSet *actionSet) : Action(new ActionPrivateVector2f(actionSet)) { } ActionVector2f::ActionVector2f(ActionSet *actionSet, const std::string &name) : Action(new ActionPrivateVector2f(actionSet)) { setName(name, name); } ActionVector2f::ActionVector2f(ActionSet *actionSet, const std::string &name, const std::string &localizedName) : Action(new ActionPrivateVector2f(actionSet)) { setName(name, localizedName); } osg::Vec2f ActionVector2f::getValue(Subaction *subaction) { auto privSubaction = Subaction::Private::get(subaction); return static_cast(Private::get(this))->getValue(privSubaction.get()); } // ActionPose ActionPose::ActionPose(ActionSet *actionSet) : Action(new ActionPrivatePose(actionSet)) { } ActionPose::ActionPose(ActionSet *actionSet, const std::string &name) : Action(new ActionPrivatePose(actionSet)) { setName(name, name); } ActionPose::ActionPose(ActionSet *actionSet, const std::string &name, const std::string &localizedName) : Action(new ActionPrivatePose(actionSet)) { setName(name, localizedName); } ActionPose::Location ActionPose::getValue(Subaction *subaction) { Location location; auto privSubaction = Subaction::Private::get(subaction); static_cast(Private::get(this))->locate(privSubaction.get(), location); return location; } ActionPose::Location::Location() : _flags((Flags)0) { } ActionPose::Location::Location(Flags flags, const osg::Quat &orientation, const osg::Vec3f &position) : _flags(flags), _orientation(orientation), _position(position) { } // ActionVibration ActionVibration::ActionVibration(ActionSet *actionSet) : Action(new ActionPrivateVibration(actionSet)) { } ActionVibration::ActionVibration(ActionSet *actionSet, const std::string &name) : Action(new ActionPrivateVibration(actionSet)) { setName(name, name); } ActionVibration::ActionVibration(ActionSet *actionSet, const std::string &name, const std::string &localizedName) : Action(new ActionPrivateVibration(actionSet)) { setName(name, localizedName); } bool ActionVibration::applyHapticFeedback(int64_t duration_ns, float frequency, float amplitude) { auto priv = static_cast(Private::get(this)); return priv->applyHapticFeedback(nullptr, duration_ns, frequency, amplitude); } bool ActionVibration::applyHapticFeedback(Subaction *subaction, int64_t duration_ns, float frequency, float amplitude) { auto privSubaction = Subaction::Private::get(subaction); auto priv = static_cast(Private::get(this)); return priv->applyHapticFeedback(privSubaction.get(), duration_ns, frequency, amplitude); } bool ActionVibration::stopHapticFeedback(Subaction *subaction) { auto privSubaction = Subaction::Private::get(subaction); auto priv = static_cast(Private::get(this)); return priv->stopHapticFeedback(privSubaction.get()); }