first commit
This commit is contained in:
Vendored
+507
@@ -0,0 +1,507 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Action.h"
|
||||
#include "ActionSet.h"
|
||||
|
||||
#include "OpenXR/Action.h"
|
||||
#include "OpenXR/Session.h"
|
||||
#include "OpenXR/Space.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
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::Private> subaction)
|
||||
{
|
||||
_updated = true;
|
||||
_subactions.insert(subaction);
|
||||
}
|
||||
|
||||
void Action::Private::cleanupInstance()
|
||||
{
|
||||
_updated = true;
|
||||
_action = nullptr;
|
||||
}
|
||||
|
||||
void Action::Private::getBoundSources(std::vector<std::string> &sourcePaths) const
|
||||
{
|
||||
OpenXR::Session *session = ActionSet::Private::get(_actionSet)->getSession();
|
||||
if (_action.valid() && session)
|
||||
{
|
||||
std::vector<XrPath> 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<std::string> &names) const
|
||||
{
|
||||
OpenXR::Session *session = ActionSet::Private::get(_actionSet)->getSession();
|
||||
if (_action.valid() && session)
|
||||
{
|
||||
std::vector<XrPath> 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 <typename T>
|
||||
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<State> ret = static_cast<T*>(_action.get())->createState(session,
|
||||
subactionPath);
|
||||
_states[subaction] = ret;
|
||||
return ret.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::map<Subaction::Private *, osg::ref_ptr<State>> _states;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ActionPrivateSimple : public ActionPrivateCommon<T>
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename T::State State;
|
||||
|
||||
ActionPrivateSimple(ActionSet *actionSet) :
|
||||
ActionPrivateCommon<T>(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<OpenXR::ActionBoolean> ActionPrivateBoolean;
|
||||
typedef ActionPrivateSimple<OpenXR::ActionFloat> ActionPrivateFloat;
|
||||
typedef ActionPrivateSimple<OpenXR::ActionVector2f> ActionPrivateVector2f;
|
||||
|
||||
class ActionPrivatePose : public ActionPrivateCommon<OpenXR::ActionPose>
|
||||
{
|
||||
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<OpenXR::ActionVibration>
|
||||
{
|
||||
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<std::string> &sourcePaths) const
|
||||
{
|
||||
_private->getBoundSources(sourcePaths);
|
||||
}
|
||||
|
||||
void Action::getBoundSourcesLocalizedNames(uint32_t whichComponents,
|
||||
std::vector<std::string> &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<ActionPrivateBoolean *>(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<ActionPrivateFloat *>(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<ActionPrivateVector2f *>(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<ActionPrivatePose *>(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<ActionPrivateVibration *>(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<ActionPrivateVibration *>(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<ActionPrivateVibration *>(Private::get(this));
|
||||
return priv->stopHapticFeedback(privSubaction.get());
|
||||
}
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_ACTION
|
||||
#define OSGXR_ACTION 1
|
||||
|
||||
#include <osgXR/Action>
|
||||
|
||||
#include "Subaction.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
class Action;
|
||||
class Instance;
|
||||
};
|
||||
|
||||
class Action::Private
|
||||
{
|
||||
public:
|
||||
|
||||
static Private *get(Action *pub)
|
||||
{
|
||||
return pub->_private.get();
|
||||
}
|
||||
|
||||
Private(ActionSet *actionSet);
|
||||
virtual ~Private();
|
||||
|
||||
void setName(const std::string &name);
|
||||
const std::string &getName() const;
|
||||
|
||||
void setLocalizedName(const std::string &localizedName);
|
||||
const std::string &getLocalizedName() const;
|
||||
|
||||
void addSubaction(std::shared_ptr<Subaction::Private> subaction);
|
||||
|
||||
bool getUpdated() const
|
||||
{
|
||||
return _updated;
|
||||
}
|
||||
|
||||
/// Setup action with an OpenXR instance
|
||||
virtual OpenXR::Action *setup(OpenXR::Instance *instance) = 0;
|
||||
/// Clean up action before an OpenXR session is destroyed
|
||||
virtual void cleanupSession() = 0;
|
||||
/// Clean up action before an OpenXR instance is destroyed
|
||||
void cleanupInstance();
|
||||
|
||||
/**
|
||||
* Get a list of currently bound source paths for this action.
|
||||
* @param sourcePaths[out] Vector of source paths to write into.
|
||||
*/
|
||||
void getBoundSources(std::vector<std::string> &sourcePaths) const;
|
||||
|
||||
/**
|
||||
* Get a list of currently bound source localized names for this action.
|
||||
* @param whichComponents Which components to include.
|
||||
* @param names[out] Vector of names to write into.
|
||||
*/
|
||||
void getBoundSourcesLocalizedNames(XrInputSourceLocalizedNameFlags whichComponents,
|
||||
std::vector<std::string> &names) const;
|
||||
|
||||
protected:
|
||||
|
||||
std::string _name;
|
||||
std::string _localizedName;
|
||||
|
||||
osg::ref_ptr<ActionSet> _actionSet;
|
||||
std::set<std::shared_ptr<Subaction::Private>> _subactions;
|
||||
|
||||
bool _updated;
|
||||
osg::ref_ptr<OpenXR::Action> _action;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+242
@@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "ActionSet.h"
|
||||
#include "Action.h"
|
||||
|
||||
#include "OpenXR/ActionSet.h"
|
||||
|
||||
#include <osgXR/Manager>
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
// Internal API
|
||||
|
||||
ActionSet::Private::Private(XRState *state) :
|
||||
_state(state),
|
||||
_priority(0),
|
||||
_updated(true)
|
||||
{
|
||||
state->addActionSet(this);
|
||||
}
|
||||
|
||||
ActionSet::Private::~Private()
|
||||
{
|
||||
XRState *state = _state.get();
|
||||
if (state)
|
||||
state->removeActionSet(this);
|
||||
}
|
||||
|
||||
void ActionSet::Private::setName(const std::string &name)
|
||||
{
|
||||
_updated = true;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
const std::string &ActionSet::Private::getName() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
void ActionSet::Private::setLocalizedName(const std::string &localizedName)
|
||||
{
|
||||
_updated = true;
|
||||
_localizedName = localizedName;
|
||||
}
|
||||
|
||||
const std::string &ActionSet::Private::getLocalizedName() const
|
||||
{
|
||||
return _localizedName;
|
||||
}
|
||||
|
||||
void ActionSet::Private::setPriority(uint32_t priority)
|
||||
{
|
||||
_updated = true;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
uint32_t ActionSet::Private::getPriority() const
|
||||
{
|
||||
return _priority;
|
||||
}
|
||||
|
||||
bool ActionSet::Private::getUpdated() const
|
||||
{
|
||||
if (_updated)
|
||||
return true;
|
||||
for (Action::Private *action: _actions)
|
||||
if (action->getUpdated())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActionSet::Private::activate(std::shared_ptr<Subaction::Private> subaction)
|
||||
{
|
||||
_activeSubactions.insert(subaction);
|
||||
|
||||
if (_actionSet.valid() && _session.valid())
|
||||
{
|
||||
OpenXR::Path path;
|
||||
if (subaction)
|
||||
path = subaction->setup(_session->getInstance());
|
||||
_session->activateActionSet(_actionSet, path);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionSet::Private::deactivate(std::shared_ptr<Subaction::Private> subaction)
|
||||
{
|
||||
_activeSubactions.erase(subaction);
|
||||
|
||||
if (_actionSet.valid() && _session.valid())
|
||||
{
|
||||
OpenXR::Path path;
|
||||
if (subaction)
|
||||
path = subaction->setup(_session->getInstance());
|
||||
_session->deactivateActionSet(_actionSet, path);
|
||||
}
|
||||
}
|
||||
|
||||
bool ActionSet::Private::isActive()
|
||||
{
|
||||
return !_activeSubactions.empty();
|
||||
}
|
||||
|
||||
void ActionSet::Private::registerAction(Action::Private *action)
|
||||
{
|
||||
_actions.insert(action);
|
||||
}
|
||||
|
||||
void ActionSet::Private::unregisterAction(Action::Private *action)
|
||||
{
|
||||
_actions.erase(action);
|
||||
}
|
||||
|
||||
OpenXR::ActionSet *ActionSet::Private::setup(OpenXR::Instance *instance)
|
||||
{
|
||||
if (_updated)
|
||||
{
|
||||
_actionSet = new OpenXR::ActionSet(instance, _name, _localizedName,
|
||||
_priority);
|
||||
_updated = false;
|
||||
}
|
||||
return _actionSet;
|
||||
}
|
||||
|
||||
bool ActionSet::Private::setup(OpenXR::Session *session)
|
||||
{
|
||||
_session = session;
|
||||
if (_actionSet.valid())
|
||||
{
|
||||
session->addActionSet(_actionSet);
|
||||
// Init all the actions
|
||||
for (Action::Private *action: _actions)
|
||||
{
|
||||
OpenXR::Action *xrAction = action->setup(session->getInstance());
|
||||
if (xrAction)
|
||||
xrAction->init();
|
||||
}
|
||||
for (auto &subaction: _activeSubactions)
|
||||
{
|
||||
OpenXR::Path path;
|
||||
if (subaction)
|
||||
path = subaction->setup(session->getInstance());
|
||||
session->activateActionSet(_actionSet, path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActionSet::Private::cleanupSession()
|
||||
{
|
||||
for (auto *action: _actions)
|
||||
action->cleanupSession();
|
||||
}
|
||||
|
||||
void ActionSet::Private::cleanupInstance()
|
||||
{
|
||||
_updated = true;
|
||||
_actionSet = nullptr;
|
||||
for (auto *action: _actions)
|
||||
action->cleanupInstance();
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
ActionSet::ActionSet(Manager *manager) :
|
||||
_private(new Private(manager->_getXrState()))
|
||||
{
|
||||
}
|
||||
|
||||
ActionSet::ActionSet(Manager *manager,
|
||||
const std::string &name) :
|
||||
_private(new Private(manager->_getXrState()))
|
||||
{
|
||||
setName(name, name);
|
||||
}
|
||||
|
||||
ActionSet::ActionSet(Manager *manager,
|
||||
const std::string &name,
|
||||
const std::string &localizedName) :
|
||||
_private(new Private(manager->_getXrState()))
|
||||
{
|
||||
setName(name, localizedName);
|
||||
}
|
||||
|
||||
ActionSet::~ActionSet()
|
||||
{
|
||||
}
|
||||
|
||||
void ActionSet::setName(const std::string &name,
|
||||
const std::string &localizedName)
|
||||
{
|
||||
_private->setName(name);
|
||||
_private->setLocalizedName(localizedName);
|
||||
}
|
||||
|
||||
void ActionSet::setName(const std::string &name)
|
||||
{
|
||||
_private->setName(name);
|
||||
}
|
||||
|
||||
const std::string &ActionSet::getName() const
|
||||
{
|
||||
return _private->getName();
|
||||
}
|
||||
|
||||
void ActionSet::setLocalizedName(const std::string &localizedName)
|
||||
{
|
||||
_private->setLocalizedName(localizedName);
|
||||
}
|
||||
|
||||
const std::string &ActionSet::getLocalizedName() const
|
||||
{
|
||||
return _private->getLocalizedName();
|
||||
}
|
||||
|
||||
void ActionSet::setPriority(uint32_t priority)
|
||||
{
|
||||
_private->setPriority(priority);
|
||||
}
|
||||
|
||||
uint32_t ActionSet::getPriority() const
|
||||
{
|
||||
return _private->getPriority();
|
||||
}
|
||||
|
||||
void ActionSet::activate(Subaction *subaction)
|
||||
{
|
||||
_private->activate(Subaction::Private::get(subaction));
|
||||
}
|
||||
|
||||
void ActionSet::deactivate(Subaction *subaction)
|
||||
{
|
||||
_private->deactivate(Subaction::Private::get(subaction));
|
||||
}
|
||||
|
||||
bool ActionSet::isActive()
|
||||
{
|
||||
return _private->isActive();
|
||||
}
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_ACTION_SET
|
||||
#define OSGXR_ACTION_SET 1
|
||||
|
||||
#include <osgXR/ActionSet>
|
||||
#include <osgXR/Action>
|
||||
|
||||
#include "OpenXR/Path.h"
|
||||
|
||||
#include "Subaction.h"
|
||||
|
||||
#include <osg/observer_ptr>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class Action;
|
||||
class XRState;
|
||||
|
||||
namespace OpenXR {
|
||||
class ActionSet;
|
||||
class Instance;
|
||||
class Session;
|
||||
};
|
||||
|
||||
class ActionSet::Private
|
||||
{
|
||||
public:
|
||||
|
||||
static Private *get(ActionSet *pub)
|
||||
{
|
||||
return pub->_private.get();
|
||||
}
|
||||
|
||||
Private(XRState *state);
|
||||
~Private();
|
||||
|
||||
void setName(const std::string &name);
|
||||
const std::string &getName() const;
|
||||
|
||||
void setLocalizedName(const std::string &localizedName);
|
||||
const std::string &getLocalizedName() const;
|
||||
|
||||
void setPriority(uint32_t priority);
|
||||
uint32_t getPriority() const;
|
||||
|
||||
bool getUpdated() const;
|
||||
|
||||
void activate(std::shared_ptr<Subaction::Private> subaction = nullptr);
|
||||
void deactivate(std::shared_ptr<Subaction::Private> subaction = nullptr);
|
||||
bool isActive();
|
||||
|
||||
void registerAction(Action::Private *action);
|
||||
void unregisterAction(Action::Private *action);
|
||||
|
||||
/// Setup action set with an OpenXR instance
|
||||
OpenXR::ActionSet *setup(OpenXR::Instance *instance);
|
||||
/// Setup action set with an OpenXR session
|
||||
bool setup(OpenXR::Session *session);
|
||||
/// Clean up action before an OpenXR session is destroyed
|
||||
void cleanupSession();
|
||||
/// Clean up action before an OpenXR instance is destroyed
|
||||
void cleanupInstance();
|
||||
|
||||
OpenXR::Session *getSession()
|
||||
{
|
||||
return _session.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState> _state;
|
||||
std::string _name;
|
||||
std::string _localizedName;
|
||||
uint32_t _priority;
|
||||
std::set<std::shared_ptr<Subaction::Private>> _activeSubactions;
|
||||
|
||||
std::set<Action::Private *> _actions;
|
||||
|
||||
bool _updated;
|
||||
osg::ref_ptr<OpenXR::ActionSet> _actionSet;
|
||||
osg::observer_ptr<OpenXR::Session> _session;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+159
@@ -0,0 +1,159 @@
|
||||
# Dependencies
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(OpenSceneGraph REQUIRED COMPONENTS osgViewer osgUtil)
|
||||
find_package(OpenXR REQUIRED)
|
||||
|
||||
# Old OpenXR SDK versions don't find Threads but Threads::Threads is an
|
||||
# INTERFACE link library of openxr_loader, so do so explicitly
|
||||
if(OpenXR_VERSION VERSION_LESS 1.0.14)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
# Public header files
|
||||
set(osgXR_HEADERS
|
||||
include/osgXR/Action
|
||||
include/osgXR/ActionSet
|
||||
include/osgXR/CompositionLayer
|
||||
include/osgXR/CompositionLayerQuad
|
||||
include/osgXR/Export
|
||||
include/osgXR/InteractionProfile
|
||||
include/osgXR/Manager
|
||||
include/osgXR/Mirror
|
||||
include/osgXR/MirrorSettings
|
||||
include/osgXR/OpenXRDisplay
|
||||
include/osgXR/Settings
|
||||
include/osgXR/SubImage
|
||||
include/osgXR/Subaction
|
||||
include/osgXR/Swapchain
|
||||
include/osgXR/View
|
||||
include/osgXR/osgXR
|
||||
)
|
||||
|
||||
# Source files
|
||||
set(osgXR_SRCS
|
||||
OpenXR/Action.cpp
|
||||
OpenXR/ActionSet.cpp
|
||||
OpenXR/Compositor.cpp
|
||||
OpenXR/EventHandler.cpp
|
||||
OpenXR/GraphicsBinding.cpp
|
||||
OpenXR/Instance.cpp
|
||||
OpenXR/InteractionProfile.cpp
|
||||
OpenXR/Path.cpp
|
||||
OpenXR/Quirks.cpp
|
||||
OpenXR/Session.cpp
|
||||
OpenXR/Space.cpp
|
||||
OpenXR/Swapchain.cpp
|
||||
OpenXR/SwapchainGroup.cpp
|
||||
OpenXR/System.cpp
|
||||
XRFramebuffer.cpp
|
||||
XRState.cpp
|
||||
XRRealizeOperation.cpp
|
||||
Action.cpp
|
||||
ActionSet.cpp
|
||||
CompositionLayer.cpp
|
||||
CompositionLayerQuad.cpp
|
||||
FrameStore.cpp
|
||||
InteractionProfile.cpp
|
||||
Manager.cpp
|
||||
Mirror.cpp
|
||||
MirrorSettings.cpp
|
||||
OpenXRDisplay.cpp
|
||||
Settings.cpp
|
||||
Subaction.cpp
|
||||
Swapchain.cpp
|
||||
View.cpp
|
||||
osgXR.cpp
|
||||
projection.cpp
|
||||
)
|
||||
|
||||
# Win32 graphics binding
|
||||
if(WIN32)
|
||||
list(APPEND osgXR_SRCS
|
||||
OpenXR/GraphicsBindingWin32.cpp
|
||||
)
|
||||
add_compile_definitions(OSGXR_USE_WIN32)
|
||||
endif()
|
||||
|
||||
# X11 graphics binding
|
||||
find_package(X11)
|
||||
if(X11_FOUND)
|
||||
list(APPEND osgXR_SRCS
|
||||
OpenXR/GraphicsBindingX11.cpp
|
||||
)
|
||||
add_compile_definitions(OSGXR_USE_X11)
|
||||
endif()
|
||||
|
||||
|
||||
# Build osgXR as a library
|
||||
add_library(osgXR ${osgXR_LIBRARY_TYPE} ${osgXR_SRCS})
|
||||
|
||||
get_target_property(osgXR_TYPE osgXR TYPE)
|
||||
if(osgXR_TYPE STREQUAL STATIC_LIBRARY)
|
||||
# Needed to switch OSGXR_EXPORT off on Windows
|
||||
set(OSGXR_STATIC_LIBRARY 1)
|
||||
endif()
|
||||
# Needed to switch OSGXR_EXPORT to dllexport on Windows
|
||||
add_compile_definitions(OSGXR_LIBRARY)
|
||||
|
||||
# Generate a "generated/Version.h" header
|
||||
set(osgXR_VERSION_HEADER "${PROJECT_BINARY_DIR}/include/generated/Version.h")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Version.h.in"
|
||||
"${osgXR_VERSION_HEADER}")
|
||||
|
||||
# Generate "osgXR/Config" header
|
||||
set(osgXR_CONFIG_HEADER "${PROJECT_BINARY_DIR}/include/osgXR/Config")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Config.in"
|
||||
"${osgXR_CONFIG_HEADER}")
|
||||
list(APPEND osgXR_HEADERS "${osgXR_CONFIG_HEADER}")
|
||||
|
||||
# Ensure required C++ standards are available
|
||||
target_compile_features(osgXR
|
||||
# smart pointers
|
||||
PUBLIC cxx_std_11
|
||||
# std::optional
|
||||
PRIVATE cxx_std_17
|
||||
)
|
||||
|
||||
# Enable compiler warnings
|
||||
if(OSGXR_WARNINGS)
|
||||
target_compile_options(osgXR PRIVATE
|
||||
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
|
||||
-Wall -pedantic
|
||||
-Wextra -Wno-missing-field-initializers
|
||||
-Wno-unused-parameter>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:
|
||||
/W4>
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(osgXR
|
||||
PRIVATE
|
||||
${PROJECT_BINARY_DIR}/include
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
${OpenXR_INCLUDE_DIR}
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
|
||||
)
|
||||
|
||||
target_link_libraries(osgXR
|
||||
PRIVATE
|
||||
${OPENGL_LIBRARIES}
|
||||
PUBLIC
|
||||
${OPENSCENEGRAPH_LIBRARIES}
|
||||
OpenXR::openxr_loader
|
||||
)
|
||||
|
||||
set_target_properties(osgXR
|
||||
PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${osgXR_SOVERSION}
|
||||
PUBLIC_HEADER "${osgXR_HEADERS}"
|
||||
INTERFACE_osgXR_MAJOR_VERSION ${osgXR_MAJOR_VERSION}
|
||||
INTERFACE_osgXR_MINOR_VERSION ${osgXR_MINOR_VERSION}
|
||||
)
|
||||
set_property(TARGET osgXR APPEND PROPERTY
|
||||
COMPATIBLE_INTERFACE_STRING osgXR_MAJOR_VERSION osgXR_MINOR_VERSION
|
||||
)
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2022 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "CompositionLayer.h"
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
// Internal API
|
||||
|
||||
CompositionLayer::Private::Private(XRState *state) :
|
||||
_state(state),
|
||||
_visible(true),
|
||||
_order(1), // in front of perspective layer
|
||||
_alphaMode(BLEND_NONE)
|
||||
{
|
||||
state->addCompositionLayer(this);
|
||||
}
|
||||
|
||||
CompositionLayer::Private::~Private()
|
||||
{
|
||||
XRState *state = _state.get();
|
||||
if (state)
|
||||
state->removeCompositionLayer(this);
|
||||
}
|
||||
|
||||
void CompositionLayer::Private::setVisible(bool visible)
|
||||
{
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
bool CompositionLayer::Private::getVisible() const
|
||||
{
|
||||
return _visible;
|
||||
}
|
||||
|
||||
void CompositionLayer::Private::setOrder(int order)
|
||||
{
|
||||
_order = order;
|
||||
// FIXME reorder
|
||||
}
|
||||
|
||||
int CompositionLayer::Private::getOrder() const
|
||||
{
|
||||
return _order;
|
||||
}
|
||||
|
||||
void CompositionLayer::Private::setAlphaMode(AlphaMode mode)
|
||||
{
|
||||
_alphaMode = mode;
|
||||
}
|
||||
|
||||
CompositionLayer::AlphaMode CompositionLayer::Private::getAlphaMode() const
|
||||
{
|
||||
return _alphaMode;
|
||||
}
|
||||
|
||||
bool CompositionLayer::Private::writeCompositionLayer(OpenXR::Session *session,
|
||||
OpenXR::CompositionLayer *layer,
|
||||
bool disableAlpha) const
|
||||
{
|
||||
if (!_visible)
|
||||
return false;
|
||||
|
||||
XrCompositionLayerFlags flags = 0;
|
||||
AlphaMode alphaMode = disableAlpha ? BLEND_NONE : _alphaMode;
|
||||
switch (alphaMode)
|
||||
{
|
||||
case BLEND_NONE:
|
||||
break;
|
||||
case BLEND_ALPHA_PREMULT:
|
||||
flags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
|
||||
break;
|
||||
case BLEND_ALPHA_UNPREMULT:
|
||||
flags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT |
|
||||
XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
layer->setLayerFlags(flags);
|
||||
layer->setSpace(session->getLocalSpace());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
CompositionLayer::CompositionLayer(Private *priv) :
|
||||
_private(priv)
|
||||
{
|
||||
}
|
||||
|
||||
CompositionLayer::~CompositionLayer()
|
||||
{
|
||||
}
|
||||
|
||||
void CompositionLayer::setVisible(bool visible)
|
||||
{
|
||||
_private->setVisible(visible);
|
||||
}
|
||||
|
||||
bool CompositionLayer::getVisible() const
|
||||
{
|
||||
return _private->getVisible();
|
||||
}
|
||||
|
||||
void CompositionLayer::setOrder(int order)
|
||||
{
|
||||
_private->setOrder(order);
|
||||
}
|
||||
|
||||
int CompositionLayer::getOrder() const
|
||||
{
|
||||
return _private->getOrder();
|
||||
}
|
||||
|
||||
void CompositionLayer::setAlphaMode(AlphaMode mode)
|
||||
{
|
||||
_private->setAlphaMode(mode);
|
||||
}
|
||||
|
||||
CompositionLayer::AlphaMode CompositionLayer::getAlphaMode() const
|
||||
{
|
||||
return _private->getAlphaMode();
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2022 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_COMPOSITION_LAYER
|
||||
#define OSGXR_COMPOSITION_LAYER 1
|
||||
|
||||
#include <osgXR/CompositionLayer>
|
||||
|
||||
#include "OpenXR/Compositor.h"
|
||||
#include "OpenXR/Session.h"
|
||||
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class XRState;
|
||||
|
||||
class CompositionLayer::Private
|
||||
{
|
||||
public:
|
||||
|
||||
static Private *get(CompositionLayer *pub)
|
||||
{
|
||||
return pub->_private.get();
|
||||
}
|
||||
|
||||
static const Private *get(const CompositionLayer *pub)
|
||||
{
|
||||
return pub->_private.get();
|
||||
}
|
||||
|
||||
Private(XRState *state);
|
||||
virtual ~Private();
|
||||
|
||||
void setVisible(bool visible);
|
||||
bool getVisible() const;
|
||||
|
||||
void setOrder(int order);
|
||||
int getOrder() const;
|
||||
|
||||
void setAlphaMode(AlphaMode mode);
|
||||
AlphaMode getAlphaMode() const;
|
||||
|
||||
/// Write to composition layer
|
||||
bool writeCompositionLayer(OpenXR::Session *session,
|
||||
OpenXR::CompositionLayer *layer,
|
||||
bool disableAlpha) const;
|
||||
|
||||
/// Setup composition layer with an OpenXR session
|
||||
virtual bool setup(OpenXR::Session *session) = 0;
|
||||
|
||||
/// Add composition layers to the frame
|
||||
virtual void endFrame(OpenXR::Session::Frame *frame) = 0;
|
||||
|
||||
/// Clean up composition layer before an OpenXR session is destroyed
|
||||
virtual void cleanupSession() = 0;
|
||||
|
||||
/// For sorting operations
|
||||
static bool compareOrder(Private *a, Private *b)
|
||||
{
|
||||
return a->_order < b->_order;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState> _state;
|
||||
|
||||
bool _visible;
|
||||
int _order;
|
||||
AlphaMode _alphaMode;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2022 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/CompositionLayerQuad>
|
||||
#include <osgXR/Manager>
|
||||
#include <osgXR/SubImage>
|
||||
#include <osgXR/Swapchain>
|
||||
|
||||
#include "OpenXR/Compositor.h"
|
||||
|
||||
#include "CompositionLayer.h"
|
||||
#include "Swapchain.h"
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
// Internal API
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class CompositionLayerPrivateQuad : public CompositionLayer::Private
|
||||
{
|
||||
public:
|
||||
|
||||
typedef CompositionLayerQuad::EyeVisibility EyeVisibility;
|
||||
|
||||
CompositionLayerPrivateQuad(XRState *state) :
|
||||
CompositionLayer::Private(state),
|
||||
_eyeVisibility(EyeVisibility::EYES_BOTH),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
_position(0.0f, 0.0f, -1.0f),
|
||||
_size(1.0f, 1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
~CompositionLayerPrivateQuad()
|
||||
{
|
||||
}
|
||||
|
||||
void setEyeVisibility(EyeVisibility eyes)
|
||||
{
|
||||
_eyeVisibility = eyes;
|
||||
}
|
||||
|
||||
EyeVisibility getEyeVisibility() const
|
||||
{
|
||||
return _eyeVisibility;
|
||||
}
|
||||
|
||||
void setSubImage(const SubImage &subImage)
|
||||
{
|
||||
_subImage = subImage;
|
||||
}
|
||||
|
||||
const SubImage &getSubImage() const
|
||||
{
|
||||
return _subImage;
|
||||
}
|
||||
|
||||
void setOrientation(const osg::Quat &quat)
|
||||
{
|
||||
_orientation = quat;
|
||||
}
|
||||
|
||||
const osg::Quat &getOrientation() const
|
||||
{
|
||||
return _orientation;
|
||||
}
|
||||
|
||||
void setPosition(const osg::Vec3f &pos)
|
||||
{
|
||||
_position = pos;
|
||||
}
|
||||
|
||||
const osg::Vec3f &getPosition() const
|
||||
{
|
||||
return _position;
|
||||
}
|
||||
|
||||
void setSize(const osg::Vec2f &size)
|
||||
{
|
||||
_size = size;
|
||||
}
|
||||
|
||||
const osg::Vec2f &getSize() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
bool setup(OpenXR::Session *session) override
|
||||
{
|
||||
Swapchain *swapchain = _subImage.getSwapchain();
|
||||
if (swapchain)
|
||||
return Swapchain::Private::get(swapchain)->setup(_state.get(), session);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool writeCompositionLayerQuad(OpenXR::Session *session,
|
||||
OpenXR::CompositionLayerQuad *layer) const
|
||||
{
|
||||
auto swapchain = Swapchain::Private::get(_subImage.getSwapchain());
|
||||
bool ret = writeCompositionLayer(session, layer,
|
||||
swapchain->getForcedAlpha() >= 1.0f);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
_quadLayer->setEyeVisibility(static_cast<XrEyeVisibility>(_eyeVisibility));
|
||||
_quadLayer->setSubImage(swapchain->convertSubImage(_subImage));
|
||||
_quadLayer->setOrientation(_orientation);
|
||||
_quadLayer->setPosition(_position);
|
||||
_quadLayer->setSize(_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void endFrame(OpenXR::Session::Frame *frame) override
|
||||
{
|
||||
Swapchain *swapchain = _subImage.getSwapchain();
|
||||
if (!swapchain)
|
||||
return;
|
||||
auto *swapchainPriv = Swapchain::Private::get(swapchain);
|
||||
if (!swapchainPriv->valid())
|
||||
return;
|
||||
|
||||
_quadLayer = new OpenXR::CompositionLayerQuad();
|
||||
if (writeCompositionLayerQuad(frame->getSession(), _quadLayer))
|
||||
frame->addLayer(_quadLayer.get());
|
||||
}
|
||||
|
||||
void cleanupSession() override
|
||||
{
|
||||
Swapchain *swapchain = _subImage.getSwapchain();
|
||||
if (swapchain)
|
||||
Swapchain::Private::get(swapchain)->cleanupSession();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
EyeVisibility _eyeVisibility;
|
||||
SubImage _subImage;
|
||||
osg::Quat _orientation;
|
||||
osg::Vec3f _position;
|
||||
osg::Vec2f _size;
|
||||
|
||||
osg::ref_ptr<OpenXR::CompositionLayerQuad> _quadLayer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
CompositionLayerQuad::CompositionLayerQuad(Manager *manager) :
|
||||
CompositionLayer(new CompositionLayerPrivateQuad(manager->_getXrState()))
|
||||
{
|
||||
}
|
||||
|
||||
CompositionLayerQuad::~CompositionLayerQuad()
|
||||
{
|
||||
}
|
||||
|
||||
void CompositionLayerQuad::setEyeVisibility(EyeVisibility eyes)
|
||||
{
|
||||
auto priv = static_cast<CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
priv->setEyeVisibility(eyes);
|
||||
}
|
||||
|
||||
CompositionLayerQuad::EyeVisibility CompositionLayerQuad::getEyeVisibility() const
|
||||
{
|
||||
auto priv = static_cast<const CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
return priv->getEyeVisibility();
|
||||
}
|
||||
|
||||
void CompositionLayerQuad::setSubImage(Swapchain *swapchain)
|
||||
{
|
||||
setSubImage((SubImage)swapchain);
|
||||
}
|
||||
|
||||
void CompositionLayerQuad::setSubImage(const SubImage &subImage)
|
||||
{
|
||||
auto priv = static_cast<CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
priv->setSubImage(subImage);
|
||||
}
|
||||
|
||||
const SubImage &CompositionLayerQuad::getSubImage() const
|
||||
{
|
||||
auto priv = static_cast<const CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
return priv->getSubImage();
|
||||
}
|
||||
|
||||
void CompositionLayerQuad::setOrientation(const osg::Quat &quat)
|
||||
{
|
||||
auto priv = static_cast<CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
priv->setOrientation(quat);
|
||||
}
|
||||
|
||||
const osg::Quat &CompositionLayerQuad::getOrientation() const
|
||||
{
|
||||
auto priv = static_cast<const CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
return priv->getOrientation();
|
||||
}
|
||||
|
||||
void CompositionLayerQuad::setPosition(const osg::Vec3f &pos)
|
||||
{
|
||||
auto priv = static_cast<CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
priv->setPosition(pos);
|
||||
}
|
||||
|
||||
const osg::Vec3f &CompositionLayerQuad::getPosition() const
|
||||
{
|
||||
auto priv = static_cast<const CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
return priv->getPosition();
|
||||
}
|
||||
|
||||
void CompositionLayerQuad::setSize(const osg::Vec2f &size)
|
||||
{
|
||||
auto priv = static_cast<CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
priv->setSize(size);
|
||||
}
|
||||
|
||||
const osg::Vec2f &CompositionLayerQuad::getSize() const
|
||||
{
|
||||
auto priv = static_cast<const CompositionLayerPrivateQuad *>(Private::get(this));
|
||||
return priv->getSize();
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// -*-c++-*-
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_Config
|
||||
#define OSGXR_Config 1
|
||||
|
||||
#cmakedefine OSGXR_STATIC_LIBRARY
|
||||
|
||||
#endif
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_FRAME_STAMPED_VECTOR
|
||||
#define OSGXR_FRAME_STAMPED_VECTOR 1
|
||||
|
||||
#include <osg/FrameStamp>
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
/**
|
||||
* Manages frame stamping of vector items.
|
||||
* Contains a vector of the chosen type, with each item stamped with a
|
||||
* FrameStamp. Items can be retrieved by index or FrameStamp.
|
||||
*/
|
||||
template <typename T>
|
||||
class FrameStampedVector
|
||||
{
|
||||
public:
|
||||
|
||||
typedef T Item;
|
||||
typedef unsigned int FrameNumber;
|
||||
typedef const osg::FrameStamp *Stamp;
|
||||
typedef std::pair<Item, FrameNumber> StampedItem;
|
||||
|
||||
void reserve(unsigned int len)
|
||||
{
|
||||
_vec.reserve(len);
|
||||
}
|
||||
|
||||
void resize(unsigned int len, Item item = Item())
|
||||
{
|
||||
_vec.resize(len, StampedItem(item, ~0));
|
||||
}
|
||||
|
||||
unsigned int size() const
|
||||
{
|
||||
return _vec.size();
|
||||
}
|
||||
|
||||
void push_back(const Item &item)
|
||||
{
|
||||
_vec.push_back(StampedItem(item, ~0));
|
||||
}
|
||||
|
||||
// operator [] provides an Item if indexed directly
|
||||
const Item &operator [] (unsigned int index) const
|
||||
{
|
||||
return _vec[index].first;
|
||||
}
|
||||
|
||||
// operator [] provides an optional Item if indexed by stamp
|
||||
std::optional<const Item> operator [] (Stamp stamp) const
|
||||
{
|
||||
int index = findStamp(stamp);
|
||||
if (index < 0)
|
||||
return std::nullopt;
|
||||
return _vec[index].first;
|
||||
}
|
||||
|
||||
int findStamp(Stamp stamp) const
|
||||
{
|
||||
unsigned int frameNumber = stamp->getFrameNumber();
|
||||
for (unsigned int i = 0; i < _vec.size(); ++i)
|
||||
if (_vec[i].second == frameNumber)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void setStamp(unsigned int index, Stamp stamp)
|
||||
{
|
||||
_vec[index].second = stamp->getFrameNumber();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<StampedItem> _vec;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "FrameStore.h"
|
||||
|
||||
#include <osg/FrameStamp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
FrameStore::FrameStore()
|
||||
{
|
||||
}
|
||||
|
||||
osg::ref_ptr<FrameStore::Frame> FrameStore::getFrame(FrameStore::Stamp stamp)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
|
||||
int index = lookupFrame(stamp);
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
|
||||
return _store[index];
|
||||
}
|
||||
|
||||
osg::ref_ptr<FrameStore::Frame> FrameStore::getFrame(FrameStore::Stamp stamp,
|
||||
OpenXR::Session *session)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
|
||||
int index = lookupFrame(stamp);
|
||||
if (index < 0)
|
||||
{
|
||||
index = blankFrame();
|
||||
// there surely shouldn't be more than 2 frames in parallel
|
||||
assert(index >= 0);
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
|
||||
osg::ref_ptr<OpenXR::Session::Frame> frame = session->waitFrame();
|
||||
if (frame.valid())
|
||||
{
|
||||
frame->setOsgFrameNumber(stamp->getFrameNumber());
|
||||
_store[index] = frame;
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
return _store[index];
|
||||
}
|
||||
|
||||
bool FrameStore::endFrame(FrameStore::Stamp stamp)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
|
||||
int index = lookupFrame(stamp);
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
_store[index]->end();
|
||||
_store[index] = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int FrameStore::lookupFrame(FrameStore::Stamp stamp) const
|
||||
{
|
||||
unsigned int frameNumber = stamp->getFrameNumber();
|
||||
for (unsigned int i = 0; i < maxFrames; ++i)
|
||||
{
|
||||
if (_store[i].valid() &&
|
||||
_store[i]->getOsgFrameNumber() == frameNumber)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FrameStore::blankFrame() const
|
||||
{
|
||||
for (unsigned int i = 0; i < maxFrames; ++i)
|
||||
if (!_store[i].valid())
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_FRAME_STORE
|
||||
#define OSGXR_FRAME_STORE 1
|
||||
|
||||
#include "OpenXR/Session.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <OpenThreads/Mutex>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osg {
|
||||
class FrameStamp;
|
||||
}
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
/**
|
||||
* Manages concurrent frames.
|
||||
* A FrameStore stores any concurrent OpenXR frames and allows them to be
|
||||
* created and retrieved in a thread-safe way based on an osg::FrameStamp.
|
||||
*/
|
||||
class FrameStore
|
||||
{
|
||||
public:
|
||||
|
||||
typedef OpenXR::Session::Frame Frame;
|
||||
typedef const osg::FrameStamp *Stamp;
|
||||
|
||||
FrameStore();
|
||||
|
||||
/// Get a frame by FrameStamp.
|
||||
osg::ref_ptr<Frame> getFrame(Stamp stamp);
|
||||
|
||||
/// Get or wait for a frame by FrameStamp.
|
||||
osg::ref_ptr<Frame> getFrame(Stamp stamp, OpenXR::Session *session);
|
||||
|
||||
/**
|
||||
* End a frame by FrameStamp.
|
||||
* @return true on success, false otherwise.
|
||||
*/
|
||||
bool endFrame(Stamp stamp);
|
||||
|
||||
protected:
|
||||
|
||||
// These return cache index or -1
|
||||
int lookupFrame(Stamp stamp) const;
|
||||
int blankFrame() const;
|
||||
|
||||
// 2 allows work to start on next frame before the prior one has ended
|
||||
static constexpr unsigned int maxFrames = 2;
|
||||
// Protected by _mutex
|
||||
osg::ref_ptr<Frame> _store[maxFrames];
|
||||
|
||||
// For access to _store
|
||||
OpenThreads::Mutex _mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Action.h"
|
||||
#include "InteractionProfile.h"
|
||||
|
||||
#include "OpenXR/InteractionProfile.h"
|
||||
#include "OpenXR/Path.h"
|
||||
#include "OpenXR/Session.h"
|
||||
|
||||
#include <osgXR/Manager>
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
// Internal API
|
||||
|
||||
InteractionProfile::Private::Private(InteractionProfile *pub,
|
||||
XRState *state,
|
||||
const std::string &vendor,
|
||||
const std::string &type) :
|
||||
_pub(pub),
|
||||
_state(state),
|
||||
_vendor(vendor),
|
||||
_type(type),
|
||||
_updated(true)
|
||||
{
|
||||
state->addInteractionProfile(this);
|
||||
}
|
||||
|
||||
InteractionProfile::Private::~Private()
|
||||
{
|
||||
XRState *state = _state.get();
|
||||
if (state)
|
||||
state->removeInteractionProfile(this);
|
||||
}
|
||||
|
||||
void InteractionProfile::Private::suggestBinding(Action *action,
|
||||
const std::string &binding)
|
||||
{
|
||||
_bindings.push_back({action, binding});
|
||||
_updated = true;
|
||||
}
|
||||
|
||||
bool InteractionProfile::Private::setup(OpenXR::Instance *instance)
|
||||
{
|
||||
// Recreate every time, as actions may have been altered and recreated
|
||||
_profile = new OpenXR::InteractionProfile(instance, _vendor.c_str(),
|
||||
_type.c_str());
|
||||
|
||||
for (Binding &binding: _bindings)
|
||||
{
|
||||
// ensure action is set up
|
||||
OpenXR::Action *action = Action::Private::get(binding.action)->setup(instance);
|
||||
if (action)
|
||||
_profile->addBinding(action, binding.binding);
|
||||
}
|
||||
|
||||
bool ret = _profile->suggestBindings();
|
||||
if (ret)
|
||||
_updated = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void InteractionProfile::Private::cleanupInstance()
|
||||
{
|
||||
_profile = nullptr;
|
||||
}
|
||||
|
||||
OpenXR::Path InteractionProfile::Private::getPath() const
|
||||
{
|
||||
if (_profile.valid())
|
||||
return _profile->getPath();
|
||||
else
|
||||
return OpenXR::Path();
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
InteractionProfile::InteractionProfile(Manager *manager,
|
||||
const std::string &vendor,
|
||||
const std::string &type) :
|
||||
_private(new Private(this, manager->_getXrState(), vendor, type))
|
||||
{
|
||||
}
|
||||
|
||||
InteractionProfile::~InteractionProfile()
|
||||
{
|
||||
}
|
||||
|
||||
const std::string &InteractionProfile::getVendor() const
|
||||
{
|
||||
return _private->getVendor();
|
||||
}
|
||||
|
||||
const std::string &InteractionProfile::getType() const
|
||||
{
|
||||
return _private->getType();
|
||||
}
|
||||
|
||||
void InteractionProfile::suggestBinding(Action *action,
|
||||
const std::string &binding)
|
||||
{
|
||||
_private->suggestBinding(action, binding);
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_INTERACTION_PROFILE
|
||||
#define OSGXR_INTERACTION_PROFILE 1
|
||||
|
||||
#include <osgXR/InteractionProfile>
|
||||
|
||||
#include <osg/observer_ptr>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class XRState;
|
||||
|
||||
namespace OpenXR {
|
||||
class InteractionProfile;
|
||||
class Path;
|
||||
class Session;
|
||||
};
|
||||
|
||||
class InteractionProfile::Private
|
||||
{
|
||||
public:
|
||||
|
||||
static Private *get(InteractionProfile *pub)
|
||||
{
|
||||
return pub->_private.get();
|
||||
}
|
||||
|
||||
Private(InteractionProfile *pub,
|
||||
XRState *newState,
|
||||
const std::string &newVendor,
|
||||
const std::string &newType);
|
||||
~Private();
|
||||
|
||||
void suggestBinding(Action *action, const std::string &binding);
|
||||
|
||||
bool getUpdated() const
|
||||
{
|
||||
return _updated;
|
||||
}
|
||||
|
||||
/// Setup bindings with an OpenXR instance
|
||||
bool setup(OpenXR::Instance *instance);
|
||||
/// Clean up bindings before an OpenXR instance is destroyed
|
||||
void cleanupInstance();
|
||||
|
||||
// Accessors
|
||||
|
||||
/// Get the public object.
|
||||
InteractionProfile *getPublic()
|
||||
{
|
||||
return _pub;
|
||||
}
|
||||
|
||||
/// Get the vendor segment of the OpenXR interaction profile path.
|
||||
const std::string &getVendor() const
|
||||
{
|
||||
return _vendor;
|
||||
}
|
||||
|
||||
/// Get the type segment of the OpenXR interaction profile path.
|
||||
const std::string &getType() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
OpenXR::Path getPath() const;
|
||||
|
||||
private:
|
||||
|
||||
InteractionProfile *_pub;
|
||||
osg::observer_ptr<XRState> _state;
|
||||
std::string _vendor;
|
||||
std::string _type;
|
||||
|
||||
struct Binding {
|
||||
osg::ref_ptr<Action> action;
|
||||
std::string binding;
|
||||
};
|
||||
std::list<Binding> _bindings;
|
||||
|
||||
bool _updated;
|
||||
osg::ref_ptr<OpenXR::InteractionProfile> _profile;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+191
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/Manager>
|
||||
#include <osgXR/Mirror>
|
||||
|
||||
#include "XRState.h"
|
||||
#include "XRRealizeOperation.h"
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
Manager::Manager() :
|
||||
_settings(Settings::instance()),
|
||||
_destroying(false),
|
||||
_state(new XRState(_settings, const_cast<Manager *>(this)))
|
||||
{
|
||||
}
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
}
|
||||
|
||||
void Manager::setVisibilityMaskNodeMasks(osg::Node::NodeMask left,
|
||||
osg::Node::NodeMask right) const
|
||||
{
|
||||
_state->setVisibilityMaskNodeMasks(left, right);
|
||||
}
|
||||
|
||||
void Manager::configure(osgViewer::View &view) const
|
||||
{
|
||||
osgViewer::ViewerBase *viewer = _viewer;
|
||||
if (!viewer)
|
||||
viewer = dynamic_cast<osgViewer::ViewerBase *>(&view);
|
||||
if (!viewer)
|
||||
return;
|
||||
|
||||
_state->setViewer(viewer);
|
||||
|
||||
// Its rather inconvenient that ViewConfig expects a const configure()
|
||||
// Just cheat and cast away the constness here
|
||||
osg::ref_ptr<XRRealizeOperation> realizeOp = new XRRealizeOperation(_state, &view);
|
||||
viewer->setRealizeOperation(realizeOp);
|
||||
if (viewer->isRealized())
|
||||
{
|
||||
osgViewer::ViewerBase::Contexts contexts;
|
||||
viewer->getContexts(contexts, true);
|
||||
if (contexts.size() > 0)
|
||||
(*realizeOp)(contexts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::update()
|
||||
{
|
||||
_state->update();
|
||||
}
|
||||
|
||||
bool Manager::checkAndResetStateChanged()
|
||||
{
|
||||
return _state->checkAndResetStateChanged();
|
||||
}
|
||||
|
||||
bool Manager::getPresent() const
|
||||
{
|
||||
return _state->getUpState() >= XRState::VRSTATE_SYSTEM;
|
||||
}
|
||||
|
||||
bool Manager::getEnabled() const
|
||||
{
|
||||
return _state->getUpState() == XRState::VRSTATE_ACTIONS;
|
||||
}
|
||||
|
||||
void Manager::setEnabled(bool enabled)
|
||||
{
|
||||
// Avoid needlessly discarding of the instance
|
||||
// SteamVR 1.15 and 1.16 have issues with xrDestroySession() hanging
|
||||
if (enabled)
|
||||
{
|
||||
_destroying = false;
|
||||
_state->setProbing(true);
|
||||
}
|
||||
else if (_destroying)
|
||||
{
|
||||
_state->setProbing(false);
|
||||
}
|
||||
|
||||
_state->setDestState(enabled ? XRState::VRSTATE_ACTIONS
|
||||
: _state->getProbingState());
|
||||
}
|
||||
|
||||
void Manager::destroyAndWait()
|
||||
{
|
||||
_destroying = true;
|
||||
setEnabled(false);
|
||||
while (_state->isStateUpdateNeeded())
|
||||
_state->update();
|
||||
}
|
||||
|
||||
bool Manager::isDestroying() const
|
||||
{
|
||||
return _destroying;
|
||||
}
|
||||
|
||||
bool Manager::isRunning() const
|
||||
{
|
||||
return _state->isRunning();
|
||||
}
|
||||
|
||||
void Manager::syncSettings()
|
||||
{
|
||||
_state->syncSettings();
|
||||
}
|
||||
|
||||
void Manager::syncActionSetup()
|
||||
{
|
||||
_state->syncActionSetup();
|
||||
}
|
||||
|
||||
bool Manager::hasValidationLayer() const
|
||||
{
|
||||
return _state->hasValidationLayer();
|
||||
}
|
||||
|
||||
bool Manager::hasDepthInfoExtension() const
|
||||
{
|
||||
return _state->hasDepthInfoExtension();
|
||||
}
|
||||
|
||||
bool Manager::hasVisibilityMaskExtension() const
|
||||
{
|
||||
return _state->hasVisibilityMaskExtension();
|
||||
}
|
||||
|
||||
const char *Manager::getRuntimeName() const
|
||||
{
|
||||
return _state->getRuntimeName();
|
||||
}
|
||||
|
||||
const char *Manager::getSystemName() const
|
||||
{
|
||||
return _state->getSystemName();
|
||||
}
|
||||
|
||||
const char *Manager::getStateString() const
|
||||
{
|
||||
return _state->getStateString();
|
||||
}
|
||||
|
||||
void Manager::onRunning()
|
||||
{
|
||||
}
|
||||
|
||||
void Manager::onStopped()
|
||||
{
|
||||
}
|
||||
|
||||
void Manager::onFocus()
|
||||
{
|
||||
}
|
||||
|
||||
void Manager::onUnfocus()
|
||||
{
|
||||
}
|
||||
|
||||
void Manager::addMirror(Mirror *mirror)
|
||||
{
|
||||
if (!_state->valid())
|
||||
{
|
||||
// handle this later, _state may not be created yet
|
||||
_mirrorQueue.push_back(mirror);
|
||||
}
|
||||
else
|
||||
{
|
||||
// init the mirror right away
|
||||
mirror->_init();
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::setupMirrorCamera(osg::Camera *camera)
|
||||
{
|
||||
addMirror(new Mirror(this, camera));
|
||||
}
|
||||
|
||||
void Manager::_setupMirrors()
|
||||
{
|
||||
// init each mirror in the queue
|
||||
while (!_mirrorQueue.empty())
|
||||
{
|
||||
_mirrorQueue.front()->_init();
|
||||
_mirrorQueue.pop_front();
|
||||
}
|
||||
}
|
||||
Vendored
+141
@@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/Manager>
|
||||
#include <osgXR/Mirror>
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
#include <osg/PolygonMode>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
Mirror::Mirror(Manager *manager, osg::Camera *camera) :
|
||||
_manager(manager),
|
||||
_camera(camera),
|
||||
_mirrorSettings(manager->_getSettings()->getMirrorSettings())
|
||||
{
|
||||
}
|
||||
|
||||
Mirror::~Mirror()
|
||||
{
|
||||
}
|
||||
|
||||
void Mirror::_init()
|
||||
{
|
||||
_camera->setAllowEventFocus(false);
|
||||
_camera->setViewMatrix(osg::Matrix::identity());
|
||||
_camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
|
||||
|
||||
// Find the mirror settings
|
||||
MirrorSettings *mirrorSettings = &_mirrorSettings;
|
||||
// but fall back to the manager's mirror settings
|
||||
if (mirrorSettings->getMirrorMode() == MirrorSettings::MIRROR_AUTOMATIC)
|
||||
mirrorSettings = &_manager->_getSettings()->getMirrorSettings();
|
||||
switch (mirrorSettings->getMirrorMode())
|
||||
{
|
||||
case MirrorSettings::MIRROR_NONE:
|
||||
// Draw nothing, but still clear the viewport
|
||||
_camera->setClearMask(GL_COLOR_BUFFER_BIT);
|
||||
break;
|
||||
case MirrorSettings::MIRROR_AUTOMATIC:
|
||||
// Fall-through: Default to MIRROR_SINGLE
|
||||
case MirrorSettings::MIRROR_SINGLE:
|
||||
{
|
||||
int viewIndex = mirrorSettings->getMirrorViewIndex();
|
||||
if (viewIndex < 0)
|
||||
viewIndex = 0;
|
||||
setupQuad(viewIndex, 0.0f, 1.0f);
|
||||
}
|
||||
break;
|
||||
case MirrorSettings::MIRROR_LEFT_RIGHT:
|
||||
for (unsigned int viewIndex = 0; viewIndex < 2; ++viewIndex)
|
||||
setupQuad(viewIndex, 0.5f * viewIndex, 0.5f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class MirrorPreDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
|
||||
MirrorPreDrawCallback(osg::ref_ptr<XRState> xrState,
|
||||
osg::ref_ptr<osg::StateSet> stateSet,
|
||||
unsigned int viewIndex) :
|
||||
_xrState(xrState),
|
||||
_stateSet(stateSet),
|
||||
_viewIndex(viewIndex)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
const osg::FrameStamp *stamp = renderInfo.getState()->getFrameStamp();
|
||||
_stateSet->setTextureAttributeAndModes(0,
|
||||
_xrState->getViewTexture(_viewIndex, stamp));
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState> _xrState;
|
||||
osg::ref_ptr<osg::StateSet> _stateSet;
|
||||
unsigned int _viewIndex;
|
||||
};
|
||||
|
||||
class MirrorPostDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
|
||||
MirrorPostDrawCallback(osg::ref_ptr<osg::StateSet> stateSet) :
|
||||
_stateSet(stateSet)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
_stateSet->removeTextureAttribute(0, osg::StateAttribute::Type::TEXTURE);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::ref_ptr<osg::StateSet> _stateSet;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void Mirror::setupQuad(unsigned int viewIndex,
|
||||
float x, float w)
|
||||
{
|
||||
XRState *xrState = _manager->_getXrState();
|
||||
|
||||
if (viewIndex >= xrState->getViewCount())
|
||||
return;
|
||||
|
||||
// Build an always-visible quad to draw the view texture on
|
||||
osg::ref_ptr<osg::Geode> quad = new osg::Geode;
|
||||
quad->setCullingActive(false);
|
||||
|
||||
XRState::TextureRect rect = xrState->getViewTextureRect(viewIndex);
|
||||
quad->addDrawable(osg::createTexturedQuadGeometry(
|
||||
osg::Vec3(x, 0.0f, 0.0f),
|
||||
osg::Vec3(w, 0.0f, 0.0f),
|
||||
osg::Vec3(0.0f, 1.0f, 0.0f),
|
||||
rect.x, rect.y,
|
||||
rect.x + rect.width, rect.y + rect.height));
|
||||
|
||||
osg::ref_ptr<osg::StateSet> state = quad->getOrCreateStateSet();
|
||||
int forceOff = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
|
||||
int forceOn = osg::StateAttribute::ON | osg::StateAttribute::PROTECTED;
|
||||
state->setMode(GL_LIGHTING, forceOff);
|
||||
state->setMode(GL_DEPTH_TEST, forceOff);
|
||||
state->setMode(GL_FRAMEBUFFER_SRGB, forceOn);
|
||||
|
||||
_camera->addChild(quad);
|
||||
|
||||
// Set a callback so we can switch the texture to the active swapchain image
|
||||
_camera->addPreDrawCallback(new MirrorPreDrawCallback(_manager->_getXrState(),
|
||||
state, viewIndex));
|
||||
_camera->addPostDrawCallback(new MirrorPostDrawCallback(state));
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/MirrorSettings>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
MirrorSettings::MirrorSettings() :
|
||||
_mirrorMode(MIRROR_AUTOMATIC),
|
||||
_mirrorViewIndex(-1)
|
||||
{
|
||||
}
|
||||
Vendored
+188
@@ -0,0 +1,188 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Action.h"
|
||||
#include "Path.h"
|
||||
#include "Session.h"
|
||||
#include "Space.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Action::Action(ActionSet *actionSet,
|
||||
const std::string &name,
|
||||
const std::string &localizedName,
|
||||
XrActionType type) :
|
||||
_actionSet(actionSet),
|
||||
_createInfo{ XR_TYPE_ACTION_CREATE_INFO },
|
||||
_action(XR_NULL_HANDLE)
|
||||
{
|
||||
strncpy(_createInfo.actionName, name.c_str(),
|
||||
XR_MAX_ACTION_NAME_SIZE - 1);
|
||||
strncpy(_createInfo.localizedActionName, localizedName.c_str(),
|
||||
XR_MAX_LOCALIZED_ACTION_NAME_SIZE - 1);
|
||||
_createInfo.actionType = type;
|
||||
}
|
||||
|
||||
Action::~Action()
|
||||
{
|
||||
if (_action != XR_NULL_HANDLE)
|
||||
{
|
||||
check(xrDestroyAction(_action),
|
||||
"Failed to destroy OpenXR action");
|
||||
}
|
||||
}
|
||||
|
||||
void Action::addSubaction(const Path &path)
|
||||
{
|
||||
assert(path.getInstance() == getInstance());
|
||||
_subactionPaths.push_back(path.getXrPath());
|
||||
}
|
||||
|
||||
bool Action::init()
|
||||
{
|
||||
if (valid())
|
||||
return true;
|
||||
|
||||
if (!_subactionPaths.empty())
|
||||
{
|
||||
_createInfo.countSubactionPaths = _subactionPaths.size();
|
||||
_createInfo.subactionPaths = _subactionPaths.data();
|
||||
}
|
||||
return check(xrCreateAction(getXrActionSet(), &_createInfo, &_action),
|
||||
"Failed to create OpenXR action");
|
||||
}
|
||||
|
||||
ActionStateBase::ActionStateBase(Action *action, Session *session,
|
||||
Path subactionPath) :
|
||||
_action(action),
|
||||
_session(session),
|
||||
_subactionPath(subactionPath),
|
||||
_valid(false),
|
||||
_syncCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
ActionStateBase::~ActionStateBase()
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionStateBase::checkUpdate()
|
||||
{
|
||||
unsigned int sessionSyncCount = _session->getActionSyncCount();
|
||||
// If an xrSyncActions has taken place, the state is out of date
|
||||
bool needsUpdate = (_syncCount < sessionSyncCount);
|
||||
// Update the counter as caller is expected to update the state
|
||||
_syncCount = sessionSyncCount;
|
||||
return needsUpdate;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonBoolean::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_BOOLEAN };
|
||||
|
||||
_valid = check(xrGetActionStateBoolean(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get boolean OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonFloat::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_FLOAT };
|
||||
|
||||
_valid = check(xrGetActionStateFloat(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get float OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonVector2f::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_VECTOR2F };
|
||||
|
||||
_valid = check(xrGetActionStateVector2f(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get vector2f OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ActionStateCommonPose::updateState()
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = _action->getXrAction();
|
||||
getInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
_state = { XR_TYPE_ACTION_STATE_POSE };
|
||||
|
||||
_valid = check(xrGetActionStatePose(_session->getXrSession(), &getInfo,
|
||||
&_state),
|
||||
"Failed to get pose OpenXR action state");
|
||||
return _valid;
|
||||
}
|
||||
|
||||
ActionStatePose::ActionStatePose(ActionPose *action, Session *session,
|
||||
Path subactionPath) :
|
||||
Base(action, session, subactionPath),
|
||||
_space(new Space(session, action, subactionPath))
|
||||
{
|
||||
}
|
||||
|
||||
ActionStatePose::~ActionStatePose()
|
||||
{
|
||||
}
|
||||
|
||||
ActionStateVibration::ActionStateVibration(ActionVibration *action,
|
||||
Session *session,
|
||||
Path subactionPath) :
|
||||
_action(action),
|
||||
_session(session),
|
||||
_subactionPath(subactionPath)
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionStateVibration::applyHapticFeedback(int64_t duration_ns,
|
||||
float frequency,
|
||||
float amplitude) const
|
||||
{
|
||||
XrHapticActionInfo actionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
actionInfo.action = _action->getXrAction();
|
||||
actionInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
|
||||
vibration.duration = duration_ns;
|
||||
vibration.frequency = frequency;
|
||||
vibration.amplitude = amplitude;
|
||||
|
||||
return check(xrApplyHapticFeedback(_session->getXrSession(), &actionInfo,
|
||||
reinterpret_cast<XrHapticBaseHeader*>(&vibration)),
|
||||
"Failed to apply haptic feedback");
|
||||
}
|
||||
|
||||
bool ActionStateVibration::stopHapticFeedback() const
|
||||
{
|
||||
XrHapticActionInfo actionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
actionInfo.action = _action->getXrAction();
|
||||
actionInfo.subactionPath = _subactionPath.getXrPath();
|
||||
|
||||
return check(xrStopHapticFeedback(_session->getXrSession(), &actionInfo),
|
||||
"Failed to stop haptic feedback");
|
||||
}
|
||||
Vendored
+371
@@ -0,0 +1,371 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_ACTION
|
||||
#define OSGXR_OPENXR_ACTION 1
|
||||
|
||||
#include "ActionSet.h"
|
||||
#include "Path.h"
|
||||
|
||||
#include <osg/Vec2f>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<ActionSet> getActionSet() const
|
||||
{
|
||||
return _actionSet;
|
||||
}
|
||||
|
||||
inline const osg::ref_ptr<Instance> 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> _actionSet;
|
||||
std::vector<XrPath> _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> _action;
|
||||
osg::ref_ptr<Session> _session;
|
||||
Path _subactionPath;
|
||||
bool _valid;
|
||||
unsigned int _syncCount;
|
||||
};
|
||||
|
||||
/// All action states have an isActive field.
|
||||
template <typename T>
|
||||
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<XrActionStateBoolean> ActionStateCommonBoolean;
|
||||
typedef ActionStateCommon<XrActionStateFloat> ActionStateCommonFloat;
|
||||
typedef ActionStateCommon<XrActionStateVector2f> ActionStateCommonVector2f;
|
||||
typedef ActionStateCommon<XrActionStatePose> ActionStateCommonPose;
|
||||
|
||||
// Convert action values to app / OSG friendly formats
|
||||
template <typename T>
|
||||
struct ActionTypeInfo;
|
||||
|
||||
// XrBool32 -> bool
|
||||
template <>
|
||||
struct ActionTypeInfo<XrActionStateBoolean>
|
||||
{
|
||||
static bool convert(XrBool32 value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool defaultValue()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// float -> float
|
||||
template <>
|
||||
struct ActionTypeInfo<XrActionStateFloat>
|
||||
{
|
||||
static float convert(float value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
static float defaultValue()
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
// XrVector2f -> osg::Vec2f
|
||||
template <>
|
||||
struct ActionTypeInfo<XrActionStateVector2f>
|
||||
{
|
||||
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 <typename T>
|
||||
class ActionStateSimple : public ActionStateCommon<T>
|
||||
{
|
||||
private:
|
||||
|
||||
typedef ActionStateCommon<T> Base;
|
||||
|
||||
public:
|
||||
|
||||
typedef ActionTypeInfo<T> 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<XrActionStateBoolean> ActionStateBoolean;
|
||||
typedef ActionStateSimple<XrActionStateFloat> ActionStateFloat;
|
||||
typedef ActionStateSimple<XrActionStateVector2f> ActionStateVector2f;
|
||||
|
||||
/// Specialise Action for a specific input type
|
||||
template <XrActionType type, typename T>
|
||||
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<State> 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<XR_ACTION_TYPE_BOOLEAN_INPUT, ActionStateBoolean> ActionBoolean;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_FLOAT_INPUT, ActionStateFloat> ActionFloat;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_VECTOR2F_INPUT, ActionStateVector2f> ActionVector2f;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_POSE_INPUT, ActionStatePose> ActionPose;
|
||||
typedef ActionTyped<XR_ACTION_TYPE_VIBRATION_OUTPUT, ActionStateVibration> ActionVibration;
|
||||
|
||||
/// Pose actions have their own way to get the pose
|
||||
class ActionStatePose : public ActionStateCommon<XrActionStatePose>
|
||||
{
|
||||
private:
|
||||
|
||||
typedef ActionStateCommon<XrActionStatePose> Base;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
ActionStatePose(ActionPose *action, Session *session,
|
||||
Path subactionPath = Path());
|
||||
~ActionStatePose();
|
||||
|
||||
// Accessors
|
||||
|
||||
Space *getSpace()
|
||||
{
|
||||
return _space.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::ref_ptr<Space> _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> _action;
|
||||
osg::ref_ptr<Session> _session;
|
||||
Path _subactionPath;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "ActionSet.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
ActionSet::ActionSet(Instance *instance,
|
||||
const std::string &name,
|
||||
const std::string &localizedName,
|
||||
uint32_t priority) :
|
||||
_instance(instance),
|
||||
_actionSet(XR_NULL_HANDLE)
|
||||
{
|
||||
XrActionSetCreateInfo createInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
||||
strncpy(createInfo.actionSetName, name.c_str(),
|
||||
XR_MAX_ACTION_SET_NAME_SIZE - 1);
|
||||
strncpy(createInfo.localizedActionSetName, localizedName.c_str(),
|
||||
XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE - 1);
|
||||
createInfo.priority = priority;
|
||||
|
||||
check(xrCreateActionSet(getXrInstance(), &createInfo, &_actionSet),
|
||||
"Failed to create OpenXR action set");
|
||||
}
|
||||
|
||||
ActionSet::~ActionSet()
|
||||
{
|
||||
if (_actionSet != XR_NULL_HANDLE)
|
||||
{
|
||||
check(xrDestroyActionSet(_actionSet),
|
||||
"Failed to destroy OpenXR action set");
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_ACTION_SET
|
||||
#define OSGXR_OPENXR_ACTION_SET 1
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class ActionSet : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
ActionSet(Instance *instance,
|
||||
const std::string &name,
|
||||
const std::string &localizedName,
|
||||
uint32_t priority);
|
||||
virtual ~ActionSet();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _actionSet != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _instance->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrActionSet getXrActionSet() const
|
||||
{
|
||||
return _actionSet;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Action set data
|
||||
osg::ref_ptr<Instance> _instance;
|
||||
XrActionSet _actionSet;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Compositor.h"
|
||||
#include "DepthInfo.h"
|
||||
#include "Space.h"
|
||||
#include "SwapchainGroupSubImage.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
// CompositionLayerProjection
|
||||
|
||||
void CompositionLayerProjection::addView(osg::ref_ptr<Session::Frame> frame, uint32_t viewIndex,
|
||||
const SwapchainGroup::SubImage &subImage,
|
||||
const DepthInfo *depthInfo)
|
||||
{
|
||||
assert(viewIndex < _projViews.size());
|
||||
|
||||
XrCompositionLayerProjectionView &projView = _projViews[viewIndex];
|
||||
projView = { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW };
|
||||
projView.pose = frame->getViewPose(viewIndex);
|
||||
projView.fov = frame->getViewFov(viewIndex);
|
||||
subImage.getXrSubImage(&projView.subImage);
|
||||
|
||||
if (depthInfo && subImage.depthValid())
|
||||
{
|
||||
// depth info
|
||||
XrCompositionLayerDepthInfoKHR &xrDepthInfo = _depthInfos[viewIndex];
|
||||
xrDepthInfo = { XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR };
|
||||
subImage.getDepthXrSubImage(&xrDepthInfo.subImage);
|
||||
xrDepthInfo.minDepth = depthInfo->getMinDepth();
|
||||
xrDepthInfo.maxDepth = depthInfo->getMaxDepth();
|
||||
xrDepthInfo.nearZ = depthInfo->getNearZ();
|
||||
xrDepthInfo.farZ = depthInfo->getFarZ();
|
||||
|
||||
// add depth info to projection view chain
|
||||
projView.next = &xrDepthInfo;
|
||||
}
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader *CompositionLayerProjection::getXr()
|
||||
{
|
||||
unsigned int validDepthInfos = 0;
|
||||
for (unsigned int i = 0; i < _projViews.size(); ++i)
|
||||
{
|
||||
auto &view = _projViews[i];
|
||||
auto &depthInfo = _depthInfos[i];
|
||||
if (view.type != XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW)
|
||||
{
|
||||
// Eek, some views have been omitted!
|
||||
OSG_WARN << "Partial projection views!" << std::endl;
|
||||
}
|
||||
|
||||
if (depthInfo.type == XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR)
|
||||
++validDepthInfos;
|
||||
}
|
||||
|
||||
// Sanity check that depth info is entirely missing or complete
|
||||
if (validDepthInfos > 0 && validDepthInfos < _projViews.size())
|
||||
{
|
||||
OSG_WARN << "Partial projection depth info, disabling depth information" << std::endl;
|
||||
for (auto &view: _projViews)
|
||||
view.next = nullptr;
|
||||
}
|
||||
|
||||
_layer.layerFlags = _layerFlags;
|
||||
_layer.space = _space->getXrSpace();
|
||||
_layer.viewCount = _projViews.size();
|
||||
_layer.views = _projViews.data();
|
||||
return reinterpret_cast<const XrCompositionLayerBaseHeader*>(&_layer);
|
||||
}
|
||||
|
||||
// CompositionLayerQuad
|
||||
|
||||
void CompositionLayerQuad::setSubImage(const SwapchainGroup::SubImage &subImage)
|
||||
{
|
||||
subImage.getXrSubImage(&_layer.subImage);
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader *CompositionLayerQuad::getXr()
|
||||
{
|
||||
_layer.layerFlags = _layerFlags;
|
||||
_layer.space = _space->getXrSpace();
|
||||
return reinterpret_cast<const XrCompositionLayerBaseHeader*>(&_layer);
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_COMPOSITOR
|
||||
#define OSGXR_OPENXR_COMPOSITOR 1
|
||||
|
||||
#include "Session.h"
|
||||
#include "Space.h"
|
||||
#include "SwapchainGroup.h"
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class DepthInfo;
|
||||
|
||||
class CompositionLayer : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
CompositionLayer() :
|
||||
_layerFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CompositionLayer()
|
||||
{
|
||||
}
|
||||
|
||||
inline XrCompositionLayerFlags getLayerFlags() const
|
||||
{
|
||||
return _layerFlags;
|
||||
}
|
||||
inline void setLayerFlags(XrCompositionLayerFlags layerFlags)
|
||||
{
|
||||
_layerFlags = layerFlags;
|
||||
}
|
||||
|
||||
inline Space *getSpace() const
|
||||
{
|
||||
return _space;
|
||||
}
|
||||
inline void setSpace(Space *space)
|
||||
{
|
||||
_space = space;
|
||||
}
|
||||
|
||||
virtual const XrCompositionLayerBaseHeader *getXr() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
XrCompositionLayerFlags _layerFlags;
|
||||
osg::ref_ptr<Space> _space;
|
||||
};
|
||||
|
||||
class CompositionLayerProjection : public CompositionLayer
|
||||
{
|
||||
public:
|
||||
|
||||
CompositionLayerProjection(unsigned int viewCount)
|
||||
{
|
||||
_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
|
||||
_layer.next = nullptr;
|
||||
_projViews.resize(viewCount);
|
||||
_depthInfos.resize(viewCount);
|
||||
}
|
||||
|
||||
virtual ~CompositionLayerProjection()
|
||||
{
|
||||
}
|
||||
|
||||
void addView(osg::ref_ptr<Session::Frame> frame, uint32_t viewIndex,
|
||||
const SwapchainGroup::SubImage &subImage,
|
||||
const DepthInfo *depthInfo = nullptr);
|
||||
|
||||
const XrCompositionLayerBaseHeader *getXr() override;
|
||||
|
||||
protected:
|
||||
|
||||
mutable XrCompositionLayerProjection _layer;
|
||||
std::vector<XrCompositionLayerProjectionView> _projViews;
|
||||
std::vector<XrCompositionLayerDepthInfoKHR> _depthInfos;
|
||||
};
|
||||
|
||||
class CompositionLayerQuad : public CompositionLayer
|
||||
{
|
||||
public:
|
||||
|
||||
CompositionLayerQuad() :
|
||||
_layer{ XR_TYPE_COMPOSITION_LAYER_QUAD }
|
||||
{
|
||||
_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
|
||||
_layer.subImage.swapchain = XR_NULL_HANDLE;
|
||||
_layer.pose.orientation = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
}
|
||||
|
||||
virtual ~CompositionLayerQuad()
|
||||
{
|
||||
}
|
||||
|
||||
inline XrEyeVisibility getEyeVisibility() const
|
||||
{
|
||||
return _layer.eyeVisibility;
|
||||
}
|
||||
inline void setEyeVisibility(XrEyeVisibility eyeVisibility)
|
||||
{
|
||||
_layer.eyeVisibility = eyeVisibility;
|
||||
}
|
||||
|
||||
void setSubImage(const SwapchainGroup::SubImage &subImage);
|
||||
|
||||
inline osg::Quat getOrientation() const
|
||||
{
|
||||
return osg::Quat(_layer.pose.orientation.x,
|
||||
_layer.pose.orientation.y,
|
||||
_layer.pose.orientation.z,
|
||||
_layer.pose.orientation.w);
|
||||
}
|
||||
inline void setOrientation(const osg::Quat &quat)
|
||||
{
|
||||
_layer.pose.orientation.x = quat.x();
|
||||
_layer.pose.orientation.y = quat.y();
|
||||
_layer.pose.orientation.z = quat.z();
|
||||
_layer.pose.orientation.w = quat.w();
|
||||
}
|
||||
|
||||
inline osg::Vec3f getPosition() const
|
||||
{
|
||||
return osg::Vec3f(_layer.pose.position.x,
|
||||
_layer.pose.position.y,
|
||||
_layer.pose.position.z);
|
||||
}
|
||||
inline void setPosition(const osg::Vec3f &pos)
|
||||
{
|
||||
_layer.pose.position.x = pos.x();
|
||||
_layer.pose.position.y = pos.y();
|
||||
_layer.pose.position.z = pos.z();
|
||||
}
|
||||
|
||||
inline osg::Vec2f getSize() const
|
||||
{
|
||||
return osg::Vec2f(_layer.size.width,
|
||||
_layer.size.height);
|
||||
}
|
||||
inline void setSize(const osg::Vec2f &size)
|
||||
{
|
||||
_layer.size.width = size.x();
|
||||
_layer.size.height = size.y();
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader *getXr() override;
|
||||
|
||||
protected:
|
||||
|
||||
mutable XrCompositionLayerQuad _layer;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_DEPTH_INFO
|
||||
#define OSGXR_OPENXR_DEPTH_INFO 1
|
||||
|
||||
#include <osg/Matrixd>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
// Represents depth information for a view
|
||||
class DepthInfo
|
||||
{
|
||||
public:
|
||||
|
||||
DepthInfo() :
|
||||
_minDepth(0),
|
||||
_maxDepth(1),
|
||||
_nearZ(1),
|
||||
_farZ(10)
|
||||
{
|
||||
}
|
||||
|
||||
// Mutators
|
||||
|
||||
void setDepthRange(float minDepth, float maxDepth)
|
||||
{
|
||||
_minDepth = minDepth;
|
||||
_maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
void setZRange(float nearZ, float farZ)
|
||||
{
|
||||
_nearZ = nearZ;
|
||||
_farZ = farZ;
|
||||
}
|
||||
|
||||
void setZRangeFromProjection(const osg::Matrixd &proj)
|
||||
{
|
||||
float left, right, bottom, top;
|
||||
proj.getFrustum(left, right, bottom, top, _nearZ, _farZ);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
float getMinDepth() const
|
||||
{
|
||||
return _minDepth;
|
||||
}
|
||||
|
||||
float getMaxDepth() const
|
||||
{
|
||||
return _maxDepth;
|
||||
}
|
||||
|
||||
float getNearZ() const
|
||||
{
|
||||
return _nearZ;
|
||||
}
|
||||
|
||||
float getFarZ() const
|
||||
{
|
||||
return _farZ;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
float _minDepth;
|
||||
float _maxDepth;
|
||||
float _nearZ;
|
||||
float _farZ;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "EventHandler.h"
|
||||
#include "Instance.h"
|
||||
#include "Session.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
void EventHandler::onEvent(Instance *instance,
|
||||
const XrEventDataBuffer *event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case XR_TYPE_EVENT_DATA_EVENTS_LOST:
|
||||
onEventsLost(instance,
|
||||
reinterpret_cast<const XrEventDataEventsLost *>(event));
|
||||
break;
|
||||
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
|
||||
onInstanceLossPending(instance,
|
||||
reinterpret_cast<const XrEventDataInstanceLossPending *>(event));
|
||||
break;
|
||||
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
|
||||
{
|
||||
auto *profileEvent = reinterpret_cast<const XrEventDataInteractionProfileChanged *>(event);
|
||||
Session *session = instance->getSession(profileEvent->session);
|
||||
if (session)
|
||||
onInteractionProfileChanged(session, profileEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR interaction profile changed event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
|
||||
{
|
||||
auto *spaceEvent = reinterpret_cast<const XrEventDataReferenceSpaceChangePending *>(event);
|
||||
Session *session = instance->getSession(spaceEvent->session);
|
||||
if (session)
|
||||
onReferenceSpaceChangePending(session, spaceEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR reference space change pending event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR:
|
||||
{
|
||||
auto *maskEvent = reinterpret_cast<const XrEventDataVisibilityMaskChangedKHR *>(event);
|
||||
Session *session = instance->getSession(maskEvent->session);
|
||||
if (session)
|
||||
onVisibilityMaskChanged(session, maskEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR visibility mask change event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
|
||||
{
|
||||
auto *stateEvent = reinterpret_cast<const XrEventDataSessionStateChanged *>(event);
|
||||
Session *session = instance->getSession(stateEvent->session);
|
||||
if (session)
|
||||
onSessionStateChanged(session, stateEvent);
|
||||
else
|
||||
OSG_WARN << "Unhandled OpenXR session state change event: Session not registered" << std::endl;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
onUnhandledEvent(instance, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::onUnhandledEvent(Instance *instance,
|
||||
const XrEventDataBuffer *event)
|
||||
{
|
||||
OSG_WARN << "Unhandled OpenXR Event: " << event->type << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onEventsLost(Instance *instance,
|
||||
const XrEventDataEventsLost *event)
|
||||
{
|
||||
OSG_WARN << event->lostEventCount << " OpenXR events lost" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onInstanceLossPending(Instance *instance,
|
||||
const XrEventDataInstanceLossPending *event)
|
||||
{
|
||||
OSG_WARN << "OpenXR instance loss pending" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onInteractionProfileChanged(Session *session,
|
||||
const XrEventDataInteractionProfileChanged *event)
|
||||
{
|
||||
OSG_WARN << "OpenXR interaction profile changed" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onReferenceSpaceChangePending(Session *session,
|
||||
const XrEventDataReferenceSpaceChangePending *event)
|
||||
{
|
||||
OSG_WARN << "OpenXR reference space change penging" << std::endl;
|
||||
}
|
||||
|
||||
void EventHandler::onVisibilityMaskChanged(Session *session,
|
||||
const XrEventDataVisibilityMaskChangedKHR *event)
|
||||
{
|
||||
session->updateVisibilityMasks(event->viewConfigurationType,
|
||||
event->viewIndex);
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateChanged(Session *session,
|
||||
const XrEventDataSessionStateChanged *event)
|
||||
{
|
||||
XrSessionState oldState = session->getState();
|
||||
session->setState(event->state);
|
||||
switch (event->state)
|
||||
{
|
||||
case XR_SESSION_STATE_IDLE:
|
||||
// Either starting or soon to be stopping
|
||||
if (oldState == XR_SESSION_STATE_UNKNOWN)
|
||||
onSessionStateStart(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_READY:
|
||||
// Session ready to begin
|
||||
onSessionStateReady(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_SYNCHRONIZED:
|
||||
// Either session synchronised or no longer visible
|
||||
break;
|
||||
case XR_SESSION_STATE_VISIBLE:
|
||||
// Either session now visible or lost focus
|
||||
if (oldState == XR_SESSION_STATE_FOCUSED)
|
||||
onSessionStateUnfocus(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_FOCUSED:
|
||||
// Session visible and in focus
|
||||
onSessionStateFocus(session);
|
||||
break;
|
||||
case XR_SESSION_STATE_STOPPING:
|
||||
// Session now stopping
|
||||
onSessionStateStopping(session, false);
|
||||
break;
|
||||
case XR_SESSION_STATE_LOSS_PENDING:
|
||||
// Session loss is pending, which can happen at any time
|
||||
if (oldState == XR_SESSION_STATE_FOCUSED)
|
||||
onSessionStateUnfocus(session);
|
||||
if (session->isRunning())
|
||||
onSessionStateStopping(session, true);
|
||||
// Attempt restart
|
||||
onSessionStateEnd(session, true);
|
||||
break;
|
||||
case XR_SESSION_STATE_EXITING:
|
||||
// Session is exiting and should be cleaned up
|
||||
onSessionStateEnd(session, false);
|
||||
break;
|
||||
default:
|
||||
OSG_WARN << "Unknown OpenXR session state: " << event->state << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateStart(Session *session)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateEnd(Session *session, bool retry)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateReady(Session *session)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateStopping(Session *session, bool loss)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateFocus(Session *session)
|
||||
{
|
||||
}
|
||||
|
||||
void EventHandler::onSessionStateUnfocus(Session *session)
|
||||
{
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_EVENT_HANDLER
|
||||
#define OSGXR_OPENXR_EVENT_HANDLER 1
|
||||
|
||||
#include <osg/Referenced>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Instance;
|
||||
class Session;
|
||||
|
||||
/// This class handles OpenXR events.
|
||||
class EventHandler : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// Instance events
|
||||
|
||||
/// Top level OpenXR event handler.
|
||||
void onEvent(Instance *instance, const XrEventDataBuffer *event);
|
||||
/// Handle an otherwise unhandled event.
|
||||
virtual void onUnhandledEvent(Instance *instance,
|
||||
const XrEventDataBuffer *event);
|
||||
|
||||
/// Handle an events lost event.
|
||||
virtual void onEventsLost(Instance *instance,
|
||||
const XrEventDataEventsLost *event);
|
||||
/// Handle an instance loss pending event.
|
||||
virtual void onInstanceLossPending(Instance *instance,
|
||||
const XrEventDataInstanceLossPending *event);
|
||||
|
||||
// Session events
|
||||
|
||||
/// Handle an interaction profile changed event.
|
||||
virtual void onInteractionProfileChanged(Session *session,
|
||||
const XrEventDataInteractionProfileChanged *event);
|
||||
/// Handle a reference space change pending event.
|
||||
virtual void onReferenceSpaceChangePending(Session *session,
|
||||
const XrEventDataReferenceSpaceChangePending *event);
|
||||
/// Handle a visibility mask change event.
|
||||
virtual void onVisibilityMaskChanged(Session *session,
|
||||
const XrEventDataVisibilityMaskChangedKHR *event);
|
||||
/// Handle a session state change event.
|
||||
virtual void onSessionStateChanged(Session *session,
|
||||
const XrEventDataSessionStateChanged *event);
|
||||
|
||||
// Session state events
|
||||
|
||||
/// Transition into initial idle state (idle, after init).
|
||||
virtual void onSessionStateStart(Session *session);
|
||||
/// Transition into ending state (exiting / loss pending, before cleanup).
|
||||
virtual void onSessionStateEnd(Session *session, bool retry);
|
||||
|
||||
/// Transition into a ready state.
|
||||
virtual void onSessionStateReady(Session *session);
|
||||
/// Transition out of running state (stopping, before end).
|
||||
virtual void onSessionStateStopping(Session *session, bool loss);
|
||||
|
||||
/// Transition into focused session state.
|
||||
virtual void onSessionStateFocus(Session *session);
|
||||
/// Transition out of focused session state.
|
||||
virtual void onSessionStateUnfocus(Session *session);
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "GraphicsBinding.h"
|
||||
#include "GraphicsBindingWin32.h"
|
||||
#include "GraphicsBindingX11.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class GraphicsBindingProxy : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
virtual ~GraphicsBindingProxy() {}
|
||||
|
||||
virtual GraphicsBinding *create(osgViewer::GraphicsWindow *window) = 0;
|
||||
};
|
||||
|
||||
template <typename GRAPHICS_BINDING>
|
||||
class GraphicsBindingProxyImpl : public GraphicsBindingProxy
|
||||
{
|
||||
protected:
|
||||
typedef GRAPHICS_BINDING Binding;
|
||||
typedef typename Binding::GraphicsWindow Window;
|
||||
|
||||
virtual ~GraphicsBindingProxyImpl() {}
|
||||
|
||||
public:
|
||||
GraphicsBinding *create(osgViewer::GraphicsWindow *window) override
|
||||
{
|
||||
Window *win = dynamic_cast<Window *>(window);
|
||||
if (!win)
|
||||
return nullptr;
|
||||
|
||||
return new Binding(win);
|
||||
}
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
typedef std::vector<osg::ref_ptr<GraphicsBindingProxy> > ProxyList;
|
||||
|
||||
static ProxyList proxies = {
|
||||
#ifdef OSGXR_USE_WIN32
|
||||
new GraphicsBindingProxyImpl<GraphicsBindingWin32>(),
|
||||
#endif
|
||||
#ifdef OSGXR_USE_X11
|
||||
new GraphicsBindingProxyImpl<GraphicsBindingX11>(),
|
||||
#endif
|
||||
};
|
||||
|
||||
osg::ref_ptr<GraphicsBinding> osgXR::OpenXR::createGraphicsBinding(osgViewer::GraphicsWindow *window)
|
||||
{
|
||||
GraphicsBinding *ret = nullptr;
|
||||
for (GraphicsBindingProxy *proxy: proxies)
|
||||
{
|
||||
ret = proxy->create(window);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_GRAPHICS_BINDING
|
||||
#define OSGXR_OPENXR_GRAPHICS_BINDING 1
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class GraphicsBinding : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
virtual ~GraphicsBinding() { }
|
||||
|
||||
virtual void *getXrGraphicsBinding() = 0;
|
||||
};
|
||||
|
||||
template <typename GRAPHICS_WINDOW, typename XR_BINDING>
|
||||
class GraphicsBindingImpl : public GraphicsBinding
|
||||
{
|
||||
public:
|
||||
typedef GRAPHICS_WINDOW GraphicsWindow;
|
||||
|
||||
GraphicsBindingImpl(GraphicsWindow *window);
|
||||
virtual ~GraphicsBindingImpl() {}
|
||||
|
||||
void *getXrGraphicsBinding() override
|
||||
{
|
||||
return &_binding;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
XR_BINDING _binding;
|
||||
};
|
||||
|
||||
osg::ref_ptr<GraphicsBinding> createGraphicsBinding(osgViewer::GraphicsWindow *window);
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "GraphicsBindingWin32.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
template <>
|
||||
GraphicsBindingWin32::GraphicsBindingImpl(osgViewer::GraphicsWindowWin32 *window) :
|
||||
_binding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR }
|
||||
{
|
||||
_binding.hDC = window->getHDC();
|
||||
_binding.hGLRC = window->getWGLContext();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_GRAPHICS_BINDING_WIN32
|
||||
#define OSGXR_OPENXR_GRAPHICS_BINDING_WIN32 1
|
||||
|
||||
#ifdef OSGXR_USE_WIN32
|
||||
|
||||
#include "GraphicsBinding.h"
|
||||
|
||||
#include <osgViewer/api/Win32/GraphicsWindowWin32>
|
||||
#include <unknwn.h>
|
||||
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#define XR_USE_PLATFORM_WIN32
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
typedef GraphicsBindingImpl<osgViewer::GraphicsWindowWin32, XrGraphicsBindingOpenGLWin32KHR> GraphicsBindingWin32;
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif // OSGXR_USE_WIN32
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "GraphicsBindingX11.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
namespace {
|
||||
|
||||
/// Class to spy on protected members of GraphicsWindowX11.
|
||||
class GraphicsWindowX11Spy : public osgViewer::GraphicsWindowX11
|
||||
{
|
||||
public:
|
||||
const XVisualInfo *getVisualInfo() const
|
||||
{
|
||||
return _visualInfo;
|
||||
}
|
||||
|
||||
const GLXFBConfig &getFBConfig() const
|
||||
{
|
||||
return _fbConfig;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <>
|
||||
GraphicsBindingX11::GraphicsBindingImpl(osgViewer::GraphicsWindowX11 *window) :
|
||||
_binding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR }
|
||||
{
|
||||
// window isn't actually of type GraphicsWindowX11Spy, but this allows us to
|
||||
// spy on protected members that don't have public accessors.
|
||||
auto spyWindow = static_cast<GraphicsWindowX11Spy *>(window);
|
||||
|
||||
_binding.xDisplay = window->getDisplay();
|
||||
_binding.visualid = spyWindow->getVisualInfo()->visualid;
|
||||
_binding.glxFBConfig = spyWindow->getFBConfig();
|
||||
_binding.glxDrawable = window->getWindow();
|
||||
_binding.glxContext = window->getContext();
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_GRAPHICS_BINDING_X11
|
||||
#define OSGXR_OPENXR_GRAPHICS_BINDING_X11 1
|
||||
|
||||
#ifdef OSGXR_USE_X11
|
||||
|
||||
#include "GraphicsBinding.h"
|
||||
|
||||
#include <osgViewer/api/X11/GraphicsWindowX11>
|
||||
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#define XR_USE_PLATFORM_XLIB
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
typedef GraphicsBindingImpl<osgViewer::GraphicsWindowX11, XrGraphicsBindingOpenGLXlibKHR> GraphicsBindingX11;
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif // OSGXR_USE_X11
|
||||
|
||||
#endif
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "EventHandler.h"
|
||||
#include "Instance.h"
|
||||
#include "Session.h"
|
||||
#include "System.h"
|
||||
#include "generated/Version.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/Version>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#define ENGINE_NAME "osgXR"
|
||||
#define ENGINE_VERSION (OSGXR_MAJOR_VERSION << 16 | \
|
||||
OSGXR_MINOR_VERSION << 8 | \
|
||||
OSGXR_PATCH_VERSION)
|
||||
#define API_VERSION XR_MAKE_VERSION(1, 0, 0)
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
static std::vector<XrApiLayerProperties> layers;
|
||||
static std::vector<XrExtensionProperties> extensions;
|
||||
|
||||
static bool enumerateLayers(bool invalidate = false)
|
||||
{
|
||||
static bool layersEnumerated = false;
|
||||
if (invalidate)
|
||||
{
|
||||
layers.resize(0);
|
||||
layersEnumerated = false;
|
||||
return false;
|
||||
}
|
||||
if (layersEnumerated)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Count layers
|
||||
uint32_t layerCount = 0;
|
||||
XrResult res = xrEnumerateApiLayerProperties(0, &layerCount, nullptr);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to count OpenXR API layers: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (layerCount)
|
||||
{
|
||||
// Allocate memory
|
||||
layers.resize(layerCount);
|
||||
for (auto &layer: layers)
|
||||
{
|
||||
layer.type = XR_TYPE_API_LAYER_PROPERTIES;
|
||||
layer.next = nullptr;
|
||||
}
|
||||
|
||||
// Enumerate layers
|
||||
res = xrEnumerateApiLayerProperties(layers.size(), &layerCount, layers.data());
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to enumerate " << layerCount
|
||||
<< " OpenXR API layers: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Layers may change at any time
|
||||
layers.resize(layerCount);
|
||||
}
|
||||
|
||||
layersEnumerated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enumerateExtensions(bool invalidate = false)
|
||||
{
|
||||
static bool extensionsEnumerated = false;
|
||||
if (invalidate)
|
||||
{
|
||||
extensions.resize(0);
|
||||
extensionsEnumerated = false;
|
||||
return false;
|
||||
}
|
||||
if (extensionsEnumerated)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Count extensions
|
||||
uint32_t extensionCount;
|
||||
XrResult res = xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionCount, nullptr);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to count OpenXR instance extensions: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extensionCount)
|
||||
{
|
||||
// Allocate memory
|
||||
extensions.resize(extensionCount);
|
||||
for (auto &extension: extensions)
|
||||
{
|
||||
extension.type = XR_TYPE_EXTENSION_PROPERTIES;
|
||||
extension.next = nullptr;
|
||||
}
|
||||
|
||||
// Enumerate extensions
|
||||
res = xrEnumerateInstanceExtensionProperties(nullptr, extensions.size(),
|
||||
&extensionCount, extensions.data());
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to enumerate " << extensionCount
|
||||
<< " OpenXR instance extensions: " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extensions may change (?)
|
||||
extensions.resize(extensionCount);
|
||||
}
|
||||
|
||||
extensionsEnumerated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Instance::invalidateLayers()
|
||||
{
|
||||
enumerateLayers(true);
|
||||
}
|
||||
|
||||
void Instance::invalidateExtensions()
|
||||
{
|
||||
enumerateExtensions(true);
|
||||
}
|
||||
|
||||
bool Instance::hasLayer(const char *name)
|
||||
{
|
||||
enumerateLayers();
|
||||
|
||||
for (auto &layer: layers)
|
||||
{
|
||||
if (!strncmp(name, layer.layerName, XR_MAX_API_LAYER_NAME_SIZE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Instance::hasExtension(const char *name)
|
||||
{
|
||||
enumerateExtensions();
|
||||
|
||||
for (auto &extension: extensions)
|
||||
{
|
||||
if (!strncmp(name, extension.extensionName, XR_MAX_EXTENSION_NAME_SIZE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Instance *Instance::instance()
|
||||
{
|
||||
static osg::ref_ptr<Instance> s_instance = new Instance();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
Instance::Instance():
|
||||
_layerValidation(false),
|
||||
_depthInfo(false),
|
||||
_visibilityMask(true),
|
||||
_instance(XR_NULL_HANDLE),
|
||||
_lost(false)
|
||||
{
|
||||
}
|
||||
|
||||
Instance::~Instance()
|
||||
{
|
||||
if (_instance != XR_NULL_HANDLE)
|
||||
{
|
||||
// Delete the systems
|
||||
for (System *system: _systems)
|
||||
{
|
||||
delete system;
|
||||
}
|
||||
|
||||
// Destroy the OpenXR instance
|
||||
XrResult res = xrDestroyInstance(_instance);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to destroy OpenXR instance" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Instance::InitResult Instance::init(const char *appName, uint32_t appVersion)
|
||||
{
|
||||
if (_instance != XR_NULL_HANDLE)
|
||||
{
|
||||
return INIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::vector<const char *> layerNames;
|
||||
std::vector<const char *> extensionNames;
|
||||
|
||||
// Enable validation layer if selected
|
||||
if (_layerValidation && hasLayer(XR_APILAYER_LUNARG_core_validation))
|
||||
{
|
||||
layerNames.push_back(XR_APILAYER_LUNARG_core_validation);
|
||||
}
|
||||
|
||||
// We need OpenGL support
|
||||
if (!hasExtension(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME))
|
||||
{
|
||||
OSG_WARN << "OpenXR runtime doesn't support XR_KHR_opengl_enable extension" << std::endl;
|
||||
return INIT_FAIL;
|
||||
}
|
||||
extensionNames.push_back(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME);
|
||||
|
||||
// Enable depth composition layer support if supported
|
||||
_supportsCompositionLayerDepth = hasExtension(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
|
||||
if (_depthInfo)
|
||||
{
|
||||
if (_supportsCompositionLayerDepth)
|
||||
extensionNames.push_back(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
|
||||
else
|
||||
_depthInfo = false;
|
||||
}
|
||||
|
||||
// Enable visibility mask support if supported
|
||||
_supportsVisibilityMask = hasExtension(XR_KHR_VISIBILITY_MASK_EXTENSION_NAME);
|
||||
if (_visibilityMask)
|
||||
{
|
||||
if (_supportsVisibilityMask)
|
||||
extensionNames.push_back(XR_KHR_VISIBILITY_MASK_EXTENSION_NAME);
|
||||
else
|
||||
_visibilityMask = false;
|
||||
}
|
||||
|
||||
// Create the instance
|
||||
XrInstanceCreateInfo info{ XR_TYPE_INSTANCE_CREATE_INFO };
|
||||
strncpy(info.applicationInfo.applicationName, appName,
|
||||
XR_MAX_APPLICATION_NAME_SIZE - 1);
|
||||
info.applicationInfo.applicationVersion = appVersion;
|
||||
strncpy(info.applicationInfo.engineName, ENGINE_NAME,
|
||||
XR_MAX_ENGINE_NAME_SIZE - 1);
|
||||
info.applicationInfo.engineVersion = ENGINE_VERSION;
|
||||
info.applicationInfo.apiVersion = API_VERSION;
|
||||
info.enabledApiLayerCount = layerNames.size();
|
||||
info.enabledApiLayerNames = layerNames.data();
|
||||
info.enabledExtensionCount = extensionNames.size();
|
||||
info.enabledExtensionNames = extensionNames.data();
|
||||
|
||||
XrResult res = xrCreateInstance(&info, &_instance);
|
||||
if (XR_FAILED(res))
|
||||
{
|
||||
OSG_WARN << "Failed to create OpenXR instance: " << res << std::endl;
|
||||
switch (res)
|
||||
{
|
||||
case XR_ERROR_RUNTIME_UNAVAILABLE:
|
||||
case XR_ERROR_RUNTIME_FAILURE: // Monado returns this when not running
|
||||
return INIT_LATER;
|
||||
|
||||
default:
|
||||
return INIT_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Log the runtime properties
|
||||
_properties.type = XR_TYPE_INSTANCE_PROPERTIES;
|
||||
_properties.next = nullptr;
|
||||
|
||||
if (XR_SUCCEEDED(xrGetInstanceProperties(_instance, &_properties)))
|
||||
{
|
||||
OSG_INFO << "OpenXR Runtime: \"" << _properties.runtimeName
|
||||
<< "\" version " << XR_VERSION_MAJOR(_properties.runtimeVersion)
|
||||
<< "." << XR_VERSION_MINOR(_properties.runtimeVersion)
|
||||
<< "." << XR_VERSION_PATCH(_properties.runtimeVersion) << std::endl;
|
||||
_quirks.probe(this);
|
||||
}
|
||||
|
||||
// Get extension functions
|
||||
_xrGetOpenGLGraphicsRequirementsKHR = (PFN_xrGetOpenGLGraphicsRequirementsKHR)getProcAddr("xrGetOpenGLGraphicsRequirementsKHR");
|
||||
if (_visibilityMask)
|
||||
_xrGetVisibilityMaskKHR = (PFN_xrGetVisibilityMaskKHR)getProcAddr("xrGetVisibilityMaskKHR");
|
||||
|
||||
return INIT_SUCCESS;
|
||||
}
|
||||
|
||||
bool Instance::check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
if (result == XR_ERROR_INSTANCE_LOST)
|
||||
_lost = true;
|
||||
|
||||
char resultName[XR_MAX_RESULT_STRING_SIZE];
|
||||
if (XR_FAILED(xrResultToString(_instance, result, resultName)))
|
||||
{
|
||||
OSG_WARN << warnMsg << ": " << result << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_WARN << warnMsg << ": " << resultName << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PFN_xrVoidFunction Instance::getProcAddr(const char *name) const
|
||||
{
|
||||
PFN_xrVoidFunction ret = nullptr;
|
||||
check(xrGetInstanceProcAddr(_instance, name, &ret),
|
||||
"Failed to get OpenXR procedure address");
|
||||
return ret;
|
||||
}
|
||||
|
||||
System *Instance::getSystem(XrFormFactor formFactor, bool *supported)
|
||||
{
|
||||
unsigned long ffId = formFactor - 1;
|
||||
if (ffId < _systems.size() && _systems[ffId])
|
||||
{
|
||||
if (supported)
|
||||
*supported = true;
|
||||
return _systems[ffId];
|
||||
}
|
||||
|
||||
XrSystemGetInfo getInfo{ XR_TYPE_SYSTEM_GET_INFO };
|
||||
getInfo.formFactor = formFactor;
|
||||
|
||||
XrSystemId systemId;
|
||||
XrResult res = xrGetSystem(_instance, &getInfo, &systemId);
|
||||
if (res == XR_ERROR_FORM_FACTOR_UNAVAILABLE)
|
||||
{
|
||||
// The system is only *TEMPORARILY* unavailable
|
||||
if (supported)
|
||||
*supported = true;
|
||||
return nullptr;
|
||||
}
|
||||
else if (check(res, "Failed to get OpenXR system"))
|
||||
{
|
||||
if (ffId >= _systems.size())
|
||||
_systems.resize(ffId+1, nullptr);
|
||||
|
||||
if (supported)
|
||||
*supported = true;
|
||||
return _systems[ffId] = new System(this, systemId);
|
||||
}
|
||||
|
||||
if (supported)
|
||||
*supported = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Instance::invalidateSystem(XrFormFactor formFactor)
|
||||
{
|
||||
unsigned long ffId = formFactor - 1;
|
||||
if (ffId < _systems.size())
|
||||
{
|
||||
delete _systems[ffId];
|
||||
_systems[ffId] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::registerSession(Session *session)
|
||||
{
|
||||
_sessions[session->getXrSession()] = session;
|
||||
}
|
||||
|
||||
void Instance::unregisterSession(Session *session)
|
||||
{
|
||||
_sessions.erase(session->getXrSession());
|
||||
}
|
||||
|
||||
Session *Instance::getSession(XrSession xrSession)
|
||||
{
|
||||
auto it = _sessions.find(xrSession);
|
||||
if (it == _sessions.end())
|
||||
return nullptr;
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
void Instance::pollEvents(EventHandler *handler)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
XrEventDataBuffer event;
|
||||
event.type = XR_TYPE_EVENT_DATA_BUFFER;
|
||||
event.next = nullptr;
|
||||
|
||||
XrResult res = xrPollEvent(_instance, &event);
|
||||
if (XR_FAILED(res))
|
||||
break;
|
||||
if (res == XR_EVENT_UNAVAILABLE)
|
||||
break;
|
||||
|
||||
handler->onEvent(this, &event);
|
||||
}
|
||||
}
|
||||
Vendored
+193
@@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_INSTANCE
|
||||
#define OSGXR_OPENXR_INSTANCE 1
|
||||
|
||||
#include "Quirks.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
#define XR_APILAYER_LUNARG_core_validation "XR_APILAYER_LUNARG_core_validation"
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class EventHandler;
|
||||
class System;
|
||||
class Session;
|
||||
|
||||
class Instance : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
static Instance *instance();
|
||||
|
||||
Instance();
|
||||
virtual ~Instance();
|
||||
|
||||
// Layers and extensions
|
||||
|
||||
static void invalidateLayers();
|
||||
static void invalidateExtensions();
|
||||
static bool hasLayer(const char *name);
|
||||
static bool hasExtension(const char *name);
|
||||
|
||||
// Instance initialisation
|
||||
|
||||
void setValidationLayer(bool layerValidation)
|
||||
{
|
||||
_layerValidation = layerValidation;
|
||||
}
|
||||
|
||||
void setDepthInfo(bool depthInfo)
|
||||
{
|
||||
_depthInfo = depthInfo;
|
||||
}
|
||||
|
||||
void setVisibilityMask(bool visibilityMask)
|
||||
{
|
||||
_visibilityMask = visibilityMask;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
/// Instance creation successful.
|
||||
INIT_SUCCESS,
|
||||
/// Instance creation not possible at the moment, try again later.
|
||||
INIT_LATER,
|
||||
/// Instance creation failed.
|
||||
INIT_FAIL,
|
||||
} InitResult;
|
||||
InitResult init(const char *appName, uint32_t appVersion);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _instance != XR_NULL_SYSTEM_ID;
|
||||
}
|
||||
|
||||
inline bool lost() const
|
||||
{
|
||||
return _lost;
|
||||
}
|
||||
|
||||
bool check(XrResult result, const char *warnMsg) const;
|
||||
|
||||
// Conversions
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
// Instance properties
|
||||
inline const char *getRuntimeName() const
|
||||
{
|
||||
return _properties.runtimeName;
|
||||
}
|
||||
inline XrVersion getRuntimeVersion() const
|
||||
{
|
||||
return _properties.runtimeVersion;
|
||||
}
|
||||
|
||||
inline bool getQuirk(Quirk quirk) const
|
||||
{
|
||||
return _quirks[quirk];
|
||||
}
|
||||
|
||||
// Extensions
|
||||
|
||||
bool supportsCompositionLayerDepth() const
|
||||
{
|
||||
return _supportsCompositionLayerDepth;
|
||||
}
|
||||
|
||||
bool supportsVisibilityMask() const
|
||||
{
|
||||
return _supportsVisibilityMask;
|
||||
}
|
||||
|
||||
PFN_xrVoidFunction getProcAddr(const char *name) const;
|
||||
|
||||
XrResult getOpenGLGraphicsRequirements(XrSystemId systemId,
|
||||
XrGraphicsRequirementsOpenGLKHR* graphicsRequirements) const
|
||||
{
|
||||
if (!_xrGetOpenGLGraphicsRequirementsKHR)
|
||||
return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return _xrGetOpenGLGraphicsRequirementsKHR(_instance, systemId,
|
||||
graphicsRequirements);
|
||||
}
|
||||
|
||||
XrResult xrGetVisibilityMask(XrSession session,
|
||||
XrViewConfigurationType viewConfigurationType,
|
||||
uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
XrVisibilityMaskKHR *visibilityMask)
|
||||
{
|
||||
if (!_xrGetVisibilityMaskKHR)
|
||||
return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
return _xrGetVisibilityMaskKHR(session, viewConfigurationType,
|
||||
viewIndex, visibilityMaskType,
|
||||
visibilityMask);
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
System *getSystem(XrFormFactor formFactor, bool *supported = nullptr);
|
||||
|
||||
// Up to caller to ensure no session
|
||||
void invalidateSystem(XrFormFactor formFactor);
|
||||
void registerSession(Session *session);
|
||||
void unregisterSession(Session *session);
|
||||
Session *getSession(XrSession xrSession);
|
||||
|
||||
// Events
|
||||
|
||||
void pollEvents(EventHandler *handler);
|
||||
|
||||
protected:
|
||||
|
||||
// Setup data
|
||||
bool _layerValidation;
|
||||
bool _depthInfo;
|
||||
bool _visibilityMask;
|
||||
|
||||
// Instance data
|
||||
XrInstance _instance;
|
||||
mutable bool _lost;
|
||||
|
||||
// Extension presence
|
||||
bool _supportsCompositionLayerDepth;
|
||||
bool _supportsVisibilityMask;
|
||||
// Extension functions
|
||||
mutable PFN_xrGetOpenGLGraphicsRequirementsKHR _xrGetOpenGLGraphicsRequirementsKHR;
|
||||
mutable PFN_xrGetVisibilityMaskKHR _xrGetVisibilityMaskKHR;
|
||||
|
||||
// Instance properties
|
||||
XrInstanceProperties _properties;
|
||||
|
||||
// Quirks
|
||||
Quirks _quirks;
|
||||
|
||||
// Systems
|
||||
mutable std::vector<System *> _systems;
|
||||
|
||||
// Sessions
|
||||
std::map<XrSession, Session *> _sessions;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "InteractionProfile.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
InteractionProfile::InteractionProfile(const Path &path) :
|
||||
_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
InteractionProfile::InteractionProfile(Instance *instance,
|
||||
const char *vendor, const char *type) :
|
||||
_path(instance, (std::string)"/interaction_profiles/" + vendor + "/" + type)
|
||||
{
|
||||
}
|
||||
|
||||
InteractionProfile::~InteractionProfile()
|
||||
{
|
||||
}
|
||||
|
||||
void InteractionProfile::addBinding(Action *action, const Path &binding)
|
||||
{
|
||||
assert(binding.getInstance() == getInstance());
|
||||
_bindings.insert(ActionBindingPair(action, binding.getXrPath()));
|
||||
}
|
||||
|
||||
bool InteractionProfile::suggestBindings()
|
||||
{
|
||||
// No bindings: nothing to do!
|
||||
if (_bindings.empty())
|
||||
return true;
|
||||
|
||||
// Construct binding vector from _bindings map
|
||||
std::vector<XrActionSuggestedBinding> bindings;
|
||||
bindings.reserve(_bindings.size());
|
||||
for (auto pair: _bindings)
|
||||
{
|
||||
if (pair.first->init())
|
||||
bindings.push_back({ pair.first->getXrAction(),
|
||||
pair.second });
|
||||
}
|
||||
|
||||
// Suggest the bindings
|
||||
XrInteractionProfileSuggestedBinding suggestedBinding{
|
||||
XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING
|
||||
};
|
||||
suggestedBinding.interactionProfile = _path.getXrPath();
|
||||
suggestedBinding.countSuggestedBindings = bindings.size();
|
||||
suggestedBinding.suggestedBindings = bindings.data();
|
||||
|
||||
return check(xrSuggestInteractionProfileBindings(getXrInstance(),
|
||||
&suggestedBinding),
|
||||
"Failed to suggest interaction profile bindings");
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_INTERACTION_PROFILE
|
||||
#define OSGXR_OPENXR_INTERACTION_PROFILE 1
|
||||
|
||||
#include "Action.h"
|
||||
#include "Path.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class InteractionProfile : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
InteractionProfile(const Path &path);
|
||||
InteractionProfile(Instance *instance,
|
||||
const char *vendor, const char *type);
|
||||
virtual ~InteractionProfile();
|
||||
|
||||
// Accessors
|
||||
|
||||
void addBinding(Action *action, const std::string &binding)
|
||||
{
|
||||
Path path(_path.getInstance(), binding);
|
||||
addBinding(action, path);
|
||||
}
|
||||
|
||||
void addBinding(Action *action, const Path &binding);
|
||||
|
||||
// returns true on success
|
||||
bool suggestBindings();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _path.check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _path.getInstance();
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _path.getXrInstance();
|
||||
}
|
||||
|
||||
inline const Path &getPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Interaction profile data
|
||||
Path _path;
|
||||
typedef std::pair<osg::ref_ptr<Action>, XrPath> ActionBindingPair;
|
||||
std::set<ActionBindingPair> _bindings;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Path.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Path::Path(Instance *instance,
|
||||
XrPath path) :
|
||||
_instance(instance),
|
||||
_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
Path::Path(Instance *instance,
|
||||
const std::string &path) :
|
||||
_instance(instance),
|
||||
_path(XR_NULL_PATH)
|
||||
{
|
||||
check(xrStringToPath(getXrInstance(), path.c_str(), &_path),
|
||||
"Failed to create OpenXR path from string");
|
||||
}
|
||||
|
||||
std::string Path::toString() const
|
||||
{
|
||||
if (!valid())
|
||||
return "";
|
||||
|
||||
uint32_t count;
|
||||
if (!check(xrPathToString(getXrInstance(), _path,
|
||||
0, &count, nullptr),
|
||||
"Failed to size OpenXR path string"))
|
||||
return "";
|
||||
std::vector<char> buffer(count);
|
||||
if (!check(xrPathToString(getXrInstance(), _path,
|
||||
buffer.size(), &count, buffer.data()),
|
||||
"Failed to get OpenXR path string"))
|
||||
return "";
|
||||
|
||||
return buffer.data();
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_PATH
|
||||
#define OSGXR_OPENXR_PATH 1
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Path
|
||||
{
|
||||
public:
|
||||
|
||||
Path(Instance *instance = nullptr, XrPath path = XR_NULL_PATH);
|
||||
Path(Instance *instance, const std::string &path);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _path != XR_NULL_PATH;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _instance->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrPath getXrPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
// Comparisons
|
||||
|
||||
bool operator == (const Path &other) const
|
||||
{
|
||||
return _path == other._path &&
|
||||
_instance == other._instance;
|
||||
}
|
||||
|
||||
bool operator != (const Path &other) const
|
||||
{
|
||||
return _path != other._path ||
|
||||
_instance != other._instance;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Path data
|
||||
osg::ref_ptr<Instance> _instance;
|
||||
XrPath _path;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Instance.h"
|
||||
#include "Quirks.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef OSGXR_USE_X11
|
||||
#define USING_X11 1
|
||||
#else
|
||||
#define USING_X11 0
|
||||
#endif
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
void Quirks::probe(Instance *instance)
|
||||
{
|
||||
static struct {
|
||||
Quirk quirk;
|
||||
const char *envName;
|
||||
|
||||
bool condition;
|
||||
const char *runtimeMatch;
|
||||
XrVersion runtimeVersionMin;
|
||||
XrVersion runtimeVersionMax;
|
||||
|
||||
const char *description;
|
||||
} quirkInfo[] = {
|
||||
#define QUIRK(NAME, COND, RUNTIME, VMIN, VMAX, LINK) \
|
||||
{ NAME, "OSGXR_"#NAME, COND, RUNTIME, VMIN, VMAX, \
|
||||
#NAME LINK }
|
||||
#define MIN_XR_VERSION XR_MAKE_VERSION(0, 0, 0)
|
||||
#define MAX_XR_VERSION XR_MAKE_VERSION(-1, -1, -1)
|
||||
#define MATCH_MONADO "Monado"
|
||||
#define MATCH_STEAMVR "SteamVR"
|
||||
|
||||
// As of 2021-12-16 Monado expects the GL context to be current.
|
||||
// See https://gitlab.freedesktop.org/monado/monado/-/issues/145
|
||||
// Fixed by https://gitlab.freedesktop.org/monado/monado/-/merge_requests/1216
|
||||
QUIRK(QUIRK_GL_CONTEXT_IGNORED,
|
||||
USING_X11,
|
||||
MATCH_MONADO, MIN_XR_VERSION, XR_MAKE_VERSION(21, 0, 0),
|
||||
" (https://gitlab.freedesktop.org/monado/monado/-/issues/145)"),
|
||||
|
||||
// Prior to 1.16.4 linux_v1.14 switched context but didn't restore.
|
||||
// The SteamVR runtimeVersion is unfortunately fairly useless here.
|
||||
QUIRK(QUIRK_GL_CONTEXT_CHANGED,
|
||||
USING_X11,
|
||||
MATCH_STEAMVR, MIN_XR_VERSION, XR_MAKE_VERSION(0, 1, 0),
|
||||
""),
|
||||
|
||||
// Since SteamVR 1.16.4 the GL context is cleared by various calls.
|
||||
// The SteamVR runtimeVersion is unfortunately fairly useless here.
|
||||
QUIRK(QUIRK_GL_CONTEXT_CLEARED,
|
||||
USING_X11,
|
||||
MATCH_STEAMVR, MIN_XR_VERSION, MAX_XR_VERSION,
|
||||
" (https://github.com/ValveSoftware/SteamVR-for-Linux/issues/421)"),
|
||||
|
||||
// Since SteamVR 1.15.x apps hang during xrDestroyInstance.
|
||||
QUIRK(QUIRK_AVOID_DESTROY_INSTANCE,
|
||||
USING_X11,
|
||||
MATCH_STEAMVR, MIN_XR_VERSION, MAX_XR_VERSION,
|
||||
" (https://github.com/ValveSoftware/SteamVR-for-Linux/issues/422)"),
|
||||
|
||||
#undef QUIRK
|
||||
};
|
||||
|
||||
const char *runtime = instance->getRuntimeName();
|
||||
XrVersion version = instance->getRuntimeVersion();
|
||||
|
||||
// Clear all quirks
|
||||
reset();
|
||||
|
||||
// Probe each quirk
|
||||
for (auto &quirk: quirkInfo)
|
||||
{
|
||||
const char *env = getenv(quirk.envName);
|
||||
if (env)
|
||||
{
|
||||
if (!strncmp(env, "0", 2))
|
||||
{
|
||||
set(quirk.quirk, false);
|
||||
}
|
||||
else if (!strncmp(env, "1", 2))
|
||||
{
|
||||
set(quirk.quirk, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
env = nullptr;
|
||||
OSG_WARN << "osgXR: Unknown value for env \""
|
||||
<< quirk.envName << "\", ignored" << std::endl;
|
||||
}
|
||||
}
|
||||
// Probe the runtime name and version
|
||||
if (!env &&
|
||||
quirk.condition &&
|
||||
!strncmp(runtime, quirk.runtimeMatch, strlen(quirk.runtimeMatch)) &&
|
||||
version >= quirk.runtimeVersionMin &&
|
||||
version <= quirk.runtimeVersionMax)
|
||||
{
|
||||
set(quirk.quirk, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Print to log any enabled quirks
|
||||
if (any())
|
||||
{
|
||||
OSG_WARN << "osgXR: OpenXR Runtime: \"" << runtime
|
||||
<< "\" version " << XR_VERSION_MAJOR(version)
|
||||
<< "." << XR_VERSION_MINOR(version)
|
||||
<< "." << XR_VERSION_PATCH(version) << std::endl;
|
||||
for (auto &quirk: quirkInfo)
|
||||
if (test(quirk.quirk))
|
||||
OSG_WARN << "osgXR: Enabling " << quirk.description << std::endl;
|
||||
}
|
||||
}
|
||||
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_QUIRKS
|
||||
#define OSGXR_OPENXR_QUIRKS 1
|
||||
|
||||
#include <bitset>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Instance;
|
||||
|
||||
typedef enum Quirk {
|
||||
/**
|
||||
* This quirk indicates that the GLX context may be assumed to be current by
|
||||
* certain XR calls.
|
||||
* The affected calls are:
|
||||
* - xrCreateSession
|
||||
* - xrCreateSwapchain
|
||||
*/
|
||||
QUIRK_GL_CONTEXT_IGNORED = 0,
|
||||
|
||||
/**
|
||||
* This quirk indicates that the GLX context may be switched but not
|
||||
* restored by certain XR calls.
|
||||
* The affected calls are:
|
||||
* - xrCreateSwapchain
|
||||
*/
|
||||
QUIRK_GL_CONTEXT_CHANGED,
|
||||
|
||||
/**
|
||||
* This quirk indicates that the GLX context may be unconditionally cleared
|
||||
* by various XR calls.
|
||||
* The affected calls are:
|
||||
* - xrCreateSwapchain
|
||||
* - xrAcquireSwapchainImage
|
||||
* - xrWaitSwapchainImage
|
||||
* - xrReleaseSwapchainImage
|
||||
* - xrEndFrame
|
||||
*/
|
||||
QUIRK_GL_CONTEXT_CLEARED,
|
||||
|
||||
/**
|
||||
* This quirk indicates that the app should avoid destroying the XR instance
|
||||
* to avoid hangs.
|
||||
*/
|
||||
QUIRK_AVOID_DESTROY_INSTANCE,
|
||||
|
||||
QUIRK_MAX
|
||||
} Quirk;
|
||||
|
||||
/**
|
||||
* Represents a set of OpenXR runtime quirks which require workarounds.
|
||||
*/
|
||||
class Quirks : public std::bitset<QUIRK_MAX>
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Probe the OpenXR instance to see which quirks are required.
|
||||
*/
|
||||
void probe(Instance *instance);
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+527
@@ -0,0 +1,527 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
#include "ActionSet.h"
|
||||
#include "Compositor.h"
|
||||
#include "Session.h"
|
||||
#include "GraphicsBinding.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#ifdef OSGXR_USE_X11
|
||||
#include <osgViewer/api/X11/GraphicsWindowX11>
|
||||
#include <GL/glx.h>
|
||||
#endif // OSGXR_USE_X11
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Session::Session(System *system,
|
||||
osgViewer::GraphicsWindow *window) :
|
||||
_window(window),
|
||||
_instance(system->getInstance()),
|
||||
_system(system),
|
||||
_session(XR_NULL_HANDLE),
|
||||
_viewConfiguration(nullptr),
|
||||
_actionSyncCount(0),
|
||||
_state(XR_SESSION_STATE_UNKNOWN),
|
||||
_running(false),
|
||||
_exiting(false),
|
||||
_readSwapchainFormats(false),
|
||||
_lastDisplayTime(0)
|
||||
{
|
||||
XrSessionCreateInfo createInfo = { XR_TYPE_SESSION_CREATE_INFO };
|
||||
createInfo.systemId = getXrSystemId();
|
||||
|
||||
// Get OpenGL graphics requirements
|
||||
XrGraphicsRequirementsOpenGLKHR req;
|
||||
req.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR;
|
||||
req.next = nullptr;
|
||||
check(_instance->getOpenGLGraphicsRequirements(getXrSystemId(), &req),
|
||||
"Failed to get OpenXR's OpenGL graphics requirements");
|
||||
// ... and pretty much ignore what it says
|
||||
|
||||
osg::ref_ptr<GraphicsBinding> graphicsBinding = createGraphicsBinding(window);
|
||||
if (graphicsBinding == nullptr)
|
||||
{
|
||||
OSG_WARN << "Failed to get OpenXR graphics binding" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
createInfo.next = graphicsBinding->getXrGraphicsBinding();
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
bool switchContext = shouldSwitchContext();
|
||||
if (switchContext)
|
||||
makeCurrent();
|
||||
if (check(xrCreateSession(getXrInstance(), &createInfo, &_session),
|
||||
"Failed to create OpenXR session"))
|
||||
{
|
||||
_instance->registerSession(this);
|
||||
}
|
||||
if (switchContext)
|
||||
releaseContext();
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
releaseGLObjects();
|
||||
}
|
||||
|
||||
void Session::releaseGLObjects(osg::State *state)
|
||||
{
|
||||
if (_session != XR_NULL_HANDLE)
|
||||
{
|
||||
_instance->unregisterSession(this);
|
||||
_localSpace = nullptr;
|
||||
// GL context must not be bound in another thread
|
||||
check(xrDestroySession(_session),
|
||||
"Failed to destroy OpenXR session");
|
||||
_session = XR_NULL_HANDLE;
|
||||
_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::addActionSet(ActionSet *actionSet)
|
||||
{
|
||||
assert(actionSet->getInstance() == getInstance());
|
||||
_actionSets.insert(actionSet);
|
||||
}
|
||||
|
||||
bool Session::attachActionSets()
|
||||
{
|
||||
assert(valid());
|
||||
if (_actionSets.empty())
|
||||
return false;
|
||||
|
||||
// Construct vector of XrActionSets
|
||||
std::vector<XrActionSet> actionSets;
|
||||
actionSets.reserve(_actionSets.size());
|
||||
for (auto actionSet: _actionSets)
|
||||
actionSets.push_back(actionSet->getXrActionSet());
|
||||
|
||||
XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
|
||||
attachInfo.countActionSets = actionSets.size();
|
||||
attachInfo.actionSets = actionSets.data();
|
||||
|
||||
return check(xrAttachSessionActionSets(_session, &attachInfo),
|
||||
"Failed to attach action sets to OpenXR session");
|
||||
}
|
||||
|
||||
Path Session::getCurrentInteractionProfile(const Path &subactionPath) const
|
||||
{
|
||||
XrInteractionProfileState interactionProfile{ XR_TYPE_INTERACTION_PROFILE_STATE };
|
||||
|
||||
if (check(xrGetCurrentInteractionProfile(_session, subactionPath.getXrPath(),
|
||||
&interactionProfile),
|
||||
"Failed to get OpenXR current interaction profile"))
|
||||
{
|
||||
return Path(getInstance(), interactionProfile.interactionProfile);
|
||||
}
|
||||
return Path();
|
||||
}
|
||||
|
||||
bool Session::getActionBoundSources(Action *action,
|
||||
std::vector<XrPath> &sourcePaths) const
|
||||
{
|
||||
if (!valid())
|
||||
return false;
|
||||
|
||||
// Count bound sources
|
||||
XrBoundSourcesForActionEnumerateInfo enumerateInfo{ XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO };
|
||||
enumerateInfo.action = action->getXrAction();
|
||||
uint32_t count;
|
||||
if (check(xrEnumerateBoundSourcesForAction(_session, &enumerateInfo,
|
||||
0, &count, nullptr),
|
||||
"Failed to count OpenXR action bound sources"))
|
||||
{
|
||||
// Resize output buffer
|
||||
sourcePaths.resize(count);
|
||||
if (!count)
|
||||
return true;
|
||||
|
||||
// Fill buffer
|
||||
if (check(xrEnumerateBoundSourcesForAction(_session, &enumerateInfo,
|
||||
sourcePaths.size(),
|
||||
&count,
|
||||
sourcePaths.data()),
|
||||
"Failed to enumerate OpenXR action bound sources"))
|
||||
{
|
||||
// Success!
|
||||
if (count < sourcePaths.size())
|
||||
sourcePaths.resize(count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Failure!
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Session::getInputSourceLocalizedName(XrPath sourcePath,
|
||||
XrInputSourceLocalizedNameFlags whichComponents) const
|
||||
{
|
||||
if (!valid())
|
||||
return "";
|
||||
|
||||
XrInputSourceLocalizedNameGetInfo getInfo{ XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO };
|
||||
getInfo.sourcePath = sourcePath;
|
||||
getInfo.whichComponents = whichComponents;
|
||||
|
||||
uint32_t count;
|
||||
if (!check(xrGetInputSourceLocalizedName(_session, &getInfo,
|
||||
0, &count, nullptr),
|
||||
"Failed to size OpenXR input source localized name string"))
|
||||
return "";
|
||||
std::vector<char> buffer(count);
|
||||
if (!check(xrGetInputSourceLocalizedName(_session, &getInfo,
|
||||
buffer.size(), &count, buffer.data()),
|
||||
"Failed to get OpenXR input source localized name string"))
|
||||
return "";
|
||||
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
void Session::activateActionSet(ActionSet *actionSet, Path subactionPath)
|
||||
{
|
||||
assert(_actionSets.count(actionSet));
|
||||
_activeActionSets.insert(ActionSetSubactionPair(actionSet, subactionPath.getXrPath()));
|
||||
}
|
||||
|
||||
void Session::deactivateActionSet(ActionSet *actionSet, Path subactionPath)
|
||||
{
|
||||
_activeActionSets.erase(ActionSetSubactionPair(actionSet, subactionPath.getXrPath()));
|
||||
}
|
||||
|
||||
bool Session::syncActions()
|
||||
{
|
||||
if (!valid())
|
||||
return false;
|
||||
|
||||
XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO };
|
||||
std::vector<XrActiveActionSet> actionSets;
|
||||
if (!_activeActionSets.empty())
|
||||
{
|
||||
// Construct vector of XrActionSets
|
||||
actionSets.reserve(_activeActionSets.size());
|
||||
for (auto actionSet: _activeActionSets)
|
||||
{
|
||||
XrActiveActionSet activeActionSet;
|
||||
activeActionSet.actionSet = actionSet.first->getXrActionSet();
|
||||
activeActionSet.subactionPath = actionSet.second;
|
||||
actionSets.push_back(activeActionSet);
|
||||
}
|
||||
|
||||
syncInfo.countActiveActionSets = actionSets.size();
|
||||
syncInfo.activeActionSets = actionSets.data();
|
||||
|
||||
bool ret = check(xrSyncActions(_session, &syncInfo),
|
||||
"Failed to sync action sets to OpenXR session");
|
||||
if (ret)
|
||||
++_actionSyncCount;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const Session::SwapchainFormats &Session::getSwapchainFormats() const
|
||||
{
|
||||
if (!_readSwapchainFormats && valid())
|
||||
{
|
||||
uint32_t formatCount;
|
||||
if (check(xrEnumerateSwapchainFormats(_session, 0, &formatCount, nullptr),
|
||||
"Failed to count OpenXR swapchain formats"))
|
||||
{
|
||||
if (formatCount)
|
||||
{
|
||||
_swapchainFormats.resize(formatCount);
|
||||
if (!check(xrEnumerateSwapchainFormats(_session, formatCount,
|
||||
&formatCount, _swapchainFormats.data()),
|
||||
"Failed to enumerate OpenXR swapchain formats"))
|
||||
{
|
||||
_swapchainFormats.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readSwapchainFormats = true;
|
||||
}
|
||||
|
||||
return _swapchainFormats;
|
||||
}
|
||||
|
||||
Space *Session::getLocalSpace()
|
||||
{
|
||||
if (!_localSpace.valid())
|
||||
_localSpace = new Space(this, XR_REFERENCE_SPACE_TYPE_LOCAL);
|
||||
|
||||
return _localSpace;
|
||||
}
|
||||
|
||||
void Session::updateVisibilityMasks(XrViewConfigurationType viewConfigurationType,
|
||||
uint32_t viewIndex)
|
||||
{
|
||||
// Session must be started ...
|
||||
if (!_viewConfiguration)
|
||||
return;
|
||||
// ... and with a matching view configuration
|
||||
if (viewConfigurationType != _viewConfiguration->getType())
|
||||
return;
|
||||
|
||||
if (viewIndex >= _viewConfiguration->getViews().size())
|
||||
return;
|
||||
VisMaskGeometryView &visMaskView = _visMaskCache[viewIndex];
|
||||
|
||||
// Regenerate cached visibility mask geometries for this viewIndex
|
||||
for (uint32_t visMaskType = 0; visMaskType < visMaskView.size(); ++visMaskType)
|
||||
if (visMaskView[visMaskType].valid())
|
||||
getVisibilityMask(viewIndex, static_cast<XrVisibilityMaskTypeKHR>(1 + visMaskType), true);
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> Session::getVisibilityMask(uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
bool force)
|
||||
{
|
||||
if (!_viewConfiguration)
|
||||
return nullptr;
|
||||
if (viewIndex >= _viewConfiguration->getViews().size())
|
||||
return nullptr;
|
||||
if (visibilityMaskType == 0 || visibilityMaskType > XR_VISIBILITY_MASK_TYPE_LINE_LOOP_KHR)
|
||||
return nullptr;
|
||||
|
||||
// Size cache to match number of views...
|
||||
if (_visMaskCache.size() == 0)
|
||||
_visMaskCache.resize(_viewConfiguration->getViews().size());
|
||||
// ... and number of vis mask types
|
||||
VisMaskGeometryView &visMaskView = _visMaskCache[viewIndex];
|
||||
if (visMaskView.size() == 0)
|
||||
visMaskView.resize(XR_VISIBILITY_MASK_TYPE_LINE_LOOP_KHR);
|
||||
// Cache hit?
|
||||
VisMaskGeometry &visMaskGeometry = visMaskView[visibilityMaskType - 1];
|
||||
if (!force && visMaskGeometry.valid())
|
||||
return visMaskGeometry;
|
||||
|
||||
// Get counts of visibility mask
|
||||
XrVisibilityMaskKHR visibilityMask{ XR_TYPE_VISIBILITY_MASK_KHR };
|
||||
XrResult res = xrGetVisibilityMask(*_viewConfiguration, viewIndex,
|
||||
visibilityMaskType, &visibilityMask);
|
||||
if (res != XR_ERROR_FUNCTION_UNSUPPORTED &&
|
||||
check(res, "Failed to size OpenXR visibility mask"))
|
||||
{
|
||||
osg::PrimitiveSet::Mode mode;
|
||||
switch (visibilityMaskType)
|
||||
{
|
||||
case XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR:
|
||||
// fall through
|
||||
case XR_VISIBILITY_MASK_TYPE_VISIBLE_TRIANGLE_MESH_KHR:
|
||||
mode = osg::PrimitiveSet::TRIANGLES;
|
||||
break;
|
||||
case XR_VISIBILITY_MASK_TYPE_LINE_LOOP_KHR:
|
||||
mode = osg::PrimitiveSet::LINE_LOOP;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate space for data
|
||||
osg::ref_ptr<osg::Vec2Array> vertices = new osg::Vec2Array(visibilityMask.vertexCountOutput);
|
||||
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(mode, visibilityMask.indexCountOutput);
|
||||
|
||||
// Get the actual data
|
||||
static_assert(sizeof((*vertices)[0]) == sizeof(XrVector2f));
|
||||
static_assert(sizeof((*indices)[0]) == sizeof(uint32_t));
|
||||
visibilityMask.vertexCapacityInput = vertices->size();
|
||||
visibilityMask.vertices = reinterpret_cast<XrVector2f *>(&vertices->front());
|
||||
visibilityMask.indexCapacityInput = indices->size();
|
||||
visibilityMask.indices = reinterpret_cast<uint32_t *>(&indices->front());
|
||||
XrResult res = xrGetVisibilityMask(*_viewConfiguration, viewIndex,
|
||||
visibilityMaskType, &visibilityMask);
|
||||
if (check(res, "Failed to get OpenXR visibility mask"))
|
||||
{
|
||||
if (!visMaskGeometry.valid())
|
||||
{
|
||||
// Create a new geometry object
|
||||
osg::Geometry *geometry = new osg::Geometry();
|
||||
geometry->setVertexArray(vertices);
|
||||
geometry->addPrimitiveSet(indices);
|
||||
visMaskGeometry = geometry;
|
||||
return geometry;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the existing geometry object
|
||||
osg::Geometry *geometry = visMaskGeometry.get();
|
||||
geometry->setVertexArray(vertices);
|
||||
geometry->setPrimitiveSet(0, indices);
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Session::checkCurrent() const
|
||||
{
|
||||
#ifdef OSGXR_USE_X11
|
||||
// Ugly X11 specific hack
|
||||
const auto *window = dynamic_cast<const osgViewer::GraphicsWindowX11*>(_window.get());
|
||||
return glXGetCurrentContext() == window->getContext();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Session::makeCurrent() const
|
||||
{
|
||||
#ifdef OSGXR_USE_X11
|
||||
_window->makeCurrentImplementation();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Session::releaseContext() const
|
||||
{
|
||||
#ifdef OSGXR_USE_X11
|
||||
_window->releaseContextImplementation();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Session::begin(const System::ViewConfiguration &viewConfiguration)
|
||||
{
|
||||
_viewConfiguration = &viewConfiguration;
|
||||
|
||||
XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
|
||||
beginInfo.primaryViewConfigurationType = viewConfiguration.getType();
|
||||
if (check(xrBeginSession(_session, &beginInfo),
|
||||
"Failed to begin OpenXR session"))
|
||||
{
|
||||
_running = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Session::end()
|
||||
{
|
||||
check(xrEndSession(_session),
|
||||
"Failed to end OpenXR session");
|
||||
_running = false;
|
||||
_viewConfiguration = nullptr;
|
||||
_visMaskCache.resize(0);
|
||||
}
|
||||
|
||||
void Session::requestExit()
|
||||
{
|
||||
_exiting = true;
|
||||
if (isRunning())
|
||||
check(xrRequestExitSession(_session),
|
||||
"Failed to request OpenXR exit");
|
||||
}
|
||||
|
||||
osg::ref_ptr<Session::Frame> Session::waitFrame()
|
||||
{
|
||||
if (_instance->lost())
|
||||
return nullptr;
|
||||
|
||||
osg::ref_ptr<Frame> frame;
|
||||
|
||||
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
|
||||
XrFrameState frameState;
|
||||
frameState.type = XR_TYPE_FRAME_STATE;
|
||||
frameState.next = nullptr;
|
||||
if (check(xrWaitFrame(_session, &frameWaitInfo, &frameState),
|
||||
"Failed to wait for OpenXR frame"))
|
||||
{
|
||||
frame = new Frame(this, &frameState);
|
||||
_lastDisplayTime = frameState.predictedDisplayTime;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
Session::Frame::Frame(osg::ref_ptr<Session> session, XrFrameState *frameState) :
|
||||
_session(session),
|
||||
_time(frameState->predictedDisplayTime),
|
||||
_period(frameState->predictedDisplayPeriod),
|
||||
_shouldRender(frameState->shouldRender),
|
||||
_osgFrameNumber(0),
|
||||
_locatedViews(false),
|
||||
_begun(false),
|
||||
_envBlendMode(XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM)
|
||||
{
|
||||
}
|
||||
|
||||
Session::Frame::~Frame()
|
||||
{
|
||||
}
|
||||
|
||||
void Session::Frame::locateViews()
|
||||
{
|
||||
// Get view locations
|
||||
XrViewLocateInfo locateInfo = { XR_TYPE_VIEW_LOCATE_INFO };
|
||||
locateInfo.viewConfigurationType = _session->getViewConfiguration()->getType();
|
||||
locateInfo.displayTime = _time;
|
||||
locateInfo.space = _session->getLocalSpace()->getXrSpace();
|
||||
|
||||
_viewState = { XR_TYPE_VIEW_STATE };
|
||||
|
||||
uint32_t viewCount;
|
||||
if (!check(xrLocateViews(_session->getXrSession(), &locateInfo, &_viewState, 0, &viewCount, nullptr),
|
||||
"Failed to count OpenXR views"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_views.resize(viewCount);
|
||||
for (auto &view: _views)
|
||||
view = { XR_TYPE_VIEW };
|
||||
if (!check(xrLocateViews(_session->getXrSession(), &locateInfo, &_viewState, _views.size(), &viewCount, _views.data()),
|
||||
"Failed to locate OpenXR views"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_locatedViews = true;
|
||||
}
|
||||
|
||||
void Session::Frame::addLayer(osg::ref_ptr<CompositionLayer> layer)
|
||||
{
|
||||
_layers.push_back(layer);
|
||||
}
|
||||
|
||||
bool Session::Frame::begin()
|
||||
{
|
||||
XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
|
||||
return _begun = check(xrBeginFrame(_session->getXrSession(), &frameBeginInfo),
|
||||
"Failed to begin OpenXR frame");
|
||||
}
|
||||
|
||||
bool Session::Frame::end()
|
||||
{
|
||||
std::vector<const XrCompositionLayerBaseHeader *> layers;
|
||||
layers.reserve(_layers.size());
|
||||
for (auto &layer: _layers)
|
||||
layers.push_back(layer->getXr());
|
||||
|
||||
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
|
||||
frameEndInfo.displayTime = _time;
|
||||
frameEndInfo.environmentBlendMode = _envBlendMode;
|
||||
frameEndInfo.layerCount = layers.size();
|
||||
frameEndInfo.layers = layers.data();
|
||||
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
bool ret = check(xrEndFrame(_session->getXrSession(), &frameEndInfo),
|
||||
"Failed to end OpenXR frame");
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return ret;
|
||||
}
|
||||
Vendored
+471
@@ -0,0 +1,471 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SESSION
|
||||
#define OSGXR_OPENXR_SESSION 1
|
||||
|
||||
#include "Path.h"
|
||||
#include "System.h"
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
#include <OpenThreads/Mutex>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Action;
|
||||
class ActionSet;
|
||||
class CompositionLayer;
|
||||
class Space;
|
||||
|
||||
class Session : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
Session(System *system, osgViewer::GraphicsWindow *window);
|
||||
// GL context must not be bound in another thread
|
||||
virtual ~Session();
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
void releaseGLObjects(osg::State *state = nullptr);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _session != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _system->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Action set attachment
|
||||
|
||||
/// Add an action set to the list.
|
||||
void addActionSet(ActionSet *actionSet);
|
||||
/**
|
||||
* Attach the added action sets to the OpenXR session.
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool attachActionSets();
|
||||
|
||||
/// Get the current interaction profile for the given subaction path.
|
||||
Path getCurrentInteractionProfile(const Path &subactionPath) const;
|
||||
|
||||
/// Get a list of bound source paths for an action.
|
||||
bool getActionBoundSources(Action *action,
|
||||
std::vector<XrPath> &sourcePaths) const;
|
||||
|
||||
/**
|
||||
* Get a localized name for an input source.
|
||||
* @param sourcePath Input source path.
|
||||
* @param whichComponents Which components to include.
|
||||
* @return Localized name string
|
||||
*/
|
||||
std::string getInputSourceLocalizedName(XrPath sourcePath,
|
||||
XrInputSourceLocalizedNameFlags whichComponents) const;
|
||||
|
||||
// Action syncing
|
||||
|
||||
/// Activate a certain action set.
|
||||
void activateActionSet(ActionSet *actionSet,
|
||||
Path subactionPath = Path());
|
||||
/// Deactivate a certain action set.
|
||||
void deactivateActionSet(ActionSet *actionSet,
|
||||
Path subactionPath = Path());
|
||||
/// Sync active action sets.
|
||||
bool syncActions();
|
||||
|
||||
/// Get the number of action sync counts that have taken place.
|
||||
unsigned int getActionSyncCount() const
|
||||
{
|
||||
return _actionSyncCount;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
// Find whether the session is ready to begin
|
||||
inline bool isReady() const
|
||||
{
|
||||
return _state == XR_SESSION_STATE_READY;
|
||||
}
|
||||
|
||||
// Find whether the session is running
|
||||
inline bool isRunning() const
|
||||
{
|
||||
return _running;
|
||||
}
|
||||
|
||||
// Find whether the session is already in the process of exiting
|
||||
inline bool isExiting() const
|
||||
{
|
||||
return _exiting;
|
||||
}
|
||||
|
||||
inline osgViewer::GraphicsWindow *getWindow() const
|
||||
{
|
||||
return _window.get();
|
||||
}
|
||||
|
||||
// State management
|
||||
|
||||
inline XrSessionState getState() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
inline void setState(XrSessionState state)
|
||||
{
|
||||
_state = state;
|
||||
}
|
||||
|
||||
|
||||
// Conversions
|
||||
|
||||
inline const osg::ref_ptr<Instance> getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline const System *getSystem() const
|
||||
{
|
||||
return _system;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _system->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrSystemId getXrSystemId() const
|
||||
{
|
||||
return _system->getXrSystemId();
|
||||
}
|
||||
|
||||
inline XrSession getXrSession()
|
||||
{
|
||||
return _session;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
typedef std::vector<int64_t> SwapchainFormats;
|
||||
const SwapchainFormats &getSwapchainFormats() const;
|
||||
|
||||
Space *getLocalSpace();
|
||||
XrTime getLastDisplayTime() const
|
||||
{
|
||||
return _lastDisplayTime;
|
||||
}
|
||||
|
||||
void updateVisibilityMasks(XrViewConfigurationType viewConfigurationType,
|
||||
uint32_t viewIndex);
|
||||
osg::ref_ptr<osg::Geometry> getVisibilityMask(uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
bool force = false);
|
||||
|
||||
// Operations
|
||||
|
||||
/**
|
||||
* Check whether the session's GLX context is current.
|
||||
* This is intended for internal use only by the functions below.
|
||||
* @return whether the session's GLX context is current.
|
||||
*/
|
||||
bool checkCurrent() const;
|
||||
|
||||
/**
|
||||
* Make the GLX context current.
|
||||
* This makes the GLX context current to workaround broken XR runtimes.
|
||||
* It should be used to ensure the context is current before XR calls
|
||||
* affected by QUIRK_GL_CONTEXT_IGNORED if shouldSwitchContext()
|
||||
* (because the XR runtime will fail to do so), and to restore the
|
||||
* context after XR calls affected by QUIRK_GL_CONTEXT_CLEARED if
|
||||
* shouldRestoreContext() or getRestoreAction() == CONTEXT_RESTORE
|
||||
* (because the XR runtime will have explicitly released the context).
|
||||
*/
|
||||
void makeCurrent() const;
|
||||
/**
|
||||
* Release the GLX context so that none is current.
|
||||
* This makes no GLX context current to workaround broken XR runtimes.
|
||||
* It should be used release the switched context after XR calls
|
||||
* affected by QUIRK_GL_CONTEXT_IGNORED if shouldSwitchContext()
|
||||
* (because we had to call makeCurrent() before the call), and after XR
|
||||
* calls affected by QUIRK_GL_CONTEXT_CHANGED if getRestoreAction() ==
|
||||
* CONTEXT_RELEASE (because the XR runtime failed to do so).
|
||||
*/
|
||||
void releaseContext() const;
|
||||
|
||||
/// Action to perform before or after XR call due to quirks.
|
||||
typedef enum ContextAction {
|
||||
/// No action is necessary.
|
||||
CONTEXT_IGNORE = 0,
|
||||
/// The GLX context should be restored.
|
||||
CONTEXT_RESTORE,
|
||||
/**
|
||||
* The GLX context should be released leaving no context current.
|
||||
* It is assumed that no other context will need restoring in its
|
||||
* place.
|
||||
*/
|
||||
CONTEXT_RELEASE
|
||||
} ContextAction;
|
||||
|
||||
/**
|
||||
* Find whether for an XR call affected by QUIRK_GL_CONTEXT_IGNORED it
|
||||
* is necessary to switch the context prior to the XR call with
|
||||
* makeCurrent(), and also release it afterwards with releaseContext.
|
||||
* @return true if the context should be switched and released,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool shouldSwitchContext() const
|
||||
{
|
||||
return _instance->getQuirk(QUIRK_GL_CONTEXT_IGNORED) &&
|
||||
!checkCurrent();
|
||||
}
|
||||
/**
|
||||
* Find whether for an XR call affected by QUIRK_GL_CONTEXT_CLEARED it
|
||||
* is necessary to restore the context after the XR call.
|
||||
* @return true if the context should be restored after,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool shouldRestoreContext() const
|
||||
{
|
||||
return _instance->getQuirk(QUIRK_GL_CONTEXT_CLEARED) &&
|
||||
checkCurrent();
|
||||
}
|
||||
/**
|
||||
* Find what GL context action to perform after an XR call affected by
|
||||
* QUIRK_GL_CONTEXT_CLEARED or QUIRK_GL_CONTEXT_CHANGED.
|
||||
* @return CONTEXT_RESTORE if the runtime may clear the context and it
|
||||
* will need restoring.
|
||||
* CONTEXT_RELEASE if the runtime may change the context and
|
||||
* fail to release it.
|
||||
* CONTEXT_IGNORE otherwise.
|
||||
*/
|
||||
ContextAction getRestoreAction() const
|
||||
{
|
||||
bool cleared = _instance->getQuirk(QUIRK_GL_CONTEXT_CLEARED);
|
||||
bool changed = _instance->getQuirk(QUIRK_GL_CONTEXT_CHANGED);
|
||||
if (cleared || changed)
|
||||
{
|
||||
bool current = checkCurrent();
|
||||
if (cleared && current)
|
||||
return CONTEXT_RESTORE;
|
||||
if (changed && !current)
|
||||
return CONTEXT_RELEASE;
|
||||
}
|
||||
return CONTEXT_IGNORE;
|
||||
}
|
||||
|
||||
|
||||
bool begin(const System::ViewConfiguration &viewConfiguration);
|
||||
void end();
|
||||
void requestExit();
|
||||
|
||||
const System::ViewConfiguration *getViewConfiguration() const
|
||||
{
|
||||
return _viewConfiguration;
|
||||
}
|
||||
|
||||
class Frame : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
Frame(osg::ref_ptr<Session> session, XrFrameState *frameState);
|
||||
|
||||
virtual ~Frame();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _session->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
inline Session *getSession()
|
||||
{
|
||||
return _session;
|
||||
}
|
||||
|
||||
inline bool shouldRender() const
|
||||
{
|
||||
return _shouldRender;
|
||||
}
|
||||
|
||||
inline bool hasBegun() const
|
||||
{
|
||||
return _begun;
|
||||
}
|
||||
|
||||
inline XrTime getTime() const
|
||||
{
|
||||
return _time;
|
||||
}
|
||||
|
||||
void locateViews();
|
||||
|
||||
void checkLocateViews()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_locateViewsMutex);
|
||||
if (!_locatedViews)
|
||||
locateViews();
|
||||
}
|
||||
|
||||
bool isOrientationValid()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT;
|
||||
}
|
||||
bool isPositionValid()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT;
|
||||
}
|
||||
bool isOrientationTracked()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_TRACKED_BIT;
|
||||
}
|
||||
bool isPositionTracked()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _viewState.viewStateFlags & XR_VIEW_STATE_POSITION_TRACKED_BIT;
|
||||
}
|
||||
uint32_t getNumViews()
|
||||
{
|
||||
checkLocateViews();
|
||||
return _views.size();
|
||||
}
|
||||
const XrFovf &getViewFov(uint32_t index)
|
||||
{
|
||||
checkLocateViews();
|
||||
return _views[index].fov;
|
||||
}
|
||||
const XrPosef &getViewPose(uint32_t index)
|
||||
{
|
||||
checkLocateViews();
|
||||
return _views[index].pose;
|
||||
}
|
||||
|
||||
// Modifiers
|
||||
|
||||
inline void setEnvBlendMode(XrEnvironmentBlendMode envBlendMode)
|
||||
{
|
||||
_envBlendMode = envBlendMode;
|
||||
}
|
||||
inline XrEnvironmentBlendMode getEnvBlendMode() const
|
||||
{
|
||||
return _envBlendMode;
|
||||
}
|
||||
|
||||
inline void setOsgFrameNumber(unsigned int osgFrameNumber)
|
||||
{
|
||||
_osgFrameNumber = osgFrameNumber;
|
||||
}
|
||||
inline unsigned int getOsgFrameNumber() const
|
||||
{
|
||||
return _osgFrameNumber;
|
||||
}
|
||||
|
||||
void addLayer(osg::ref_ptr<CompositionLayer> layer);
|
||||
|
||||
// Operations
|
||||
|
||||
bool begin();
|
||||
bool end();
|
||||
|
||||
protected:
|
||||
|
||||
// Frame info
|
||||
osg::ref_ptr<Session> _session;
|
||||
XrTime _time;
|
||||
XrDuration _period;
|
||||
bool _shouldRender;
|
||||
|
||||
// OpenSceneGraph frame
|
||||
unsigned int _osgFrameNumber;
|
||||
|
||||
// For access to _locatedViews etc
|
||||
OpenThreads::Mutex _locateViewsMutex;
|
||||
|
||||
// View locations (protected by _locateViewsMutex)
|
||||
bool _locatedViews;
|
||||
XrViewState _viewState;
|
||||
std::vector<XrView> _views;
|
||||
|
||||
// Frame end info
|
||||
bool _begun;
|
||||
XrEnvironmentBlendMode _envBlendMode;
|
||||
std::vector<osg::ref_ptr<CompositionLayer> > _layers;
|
||||
};
|
||||
|
||||
osg::ref_ptr<Frame> waitFrame();
|
||||
|
||||
// OpenXR extension wrappers
|
||||
XrResult xrGetVisibilityMask(const System::ViewConfiguration &viewConfiguration,
|
||||
uint32_t viewIndex,
|
||||
XrVisibilityMaskTypeKHR visibilityMaskType,
|
||||
XrVisibilityMaskKHR *visibilityMask)
|
||||
{
|
||||
return _instance->xrGetVisibilityMask(_session,
|
||||
viewConfiguration.getType(),
|
||||
viewIndex, visibilityMaskType,
|
||||
visibilityMask);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Init data
|
||||
osg::observer_ptr<osgViewer::GraphicsWindow> _window;
|
||||
|
||||
// Session data
|
||||
osg::ref_ptr<Instance> _instance;
|
||||
const System *_system;
|
||||
XrSession _session;
|
||||
const System::ViewConfiguration *_viewConfiguration;
|
||||
|
||||
// Action sets
|
||||
std::set<osg::ref_ptr<ActionSet>> _actionSets;
|
||||
typedef std::pair<ActionSet *, XrPath> ActionSetSubactionPair;
|
||||
std::set<ActionSetSubactionPair> _activeActionSets;
|
||||
unsigned int _actionSyncCount;
|
||||
|
||||
// Session state
|
||||
XrSessionState _state;
|
||||
bool _running;
|
||||
bool _exiting;
|
||||
|
||||
// Swapchain formats
|
||||
mutable bool _readSwapchainFormats;
|
||||
mutable SwapchainFormats _swapchainFormats;
|
||||
|
||||
// Reference spaces
|
||||
osg::ref_ptr<Space> _localSpace;
|
||||
XrTime _lastDisplayTime;
|
||||
|
||||
/*
|
||||
* Visibility mask geometry cache.
|
||||
* We keep visibility mask geometries cached to avoid duplication and so
|
||||
* we can update them after a VisibilityMaskChangedKHR event.
|
||||
*/
|
||||
typedef osg::ref_ptr<osg::Geometry> VisMaskGeometry;
|
||||
typedef std::vector<VisMaskGeometry> VisMaskGeometryView;
|
||||
typedef std::vector<VisMaskGeometryView> VisMaskGeometries;
|
||||
VisMaskGeometries _visMaskCache;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Space.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
static XrPosef poseIdentity = { { 0.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.0f } };
|
||||
|
||||
Space::Space(Session *session, XrReferenceSpaceType type) :
|
||||
_session(session),
|
||||
_space(XR_NULL_HANDLE)
|
||||
{
|
||||
// Attempt to create a reference space
|
||||
XrReferenceSpaceCreateInfo createInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
|
||||
createInfo.referenceSpaceType = type;
|
||||
createInfo.poseInReferenceSpace = poseIdentity;
|
||||
|
||||
check(xrCreateReferenceSpace(session->getXrSession(), &createInfo, &_space),
|
||||
"Failed to create OpenXR reference space");
|
||||
}
|
||||
|
||||
Space::Space(Session *session, ActionPose *action,
|
||||
Path subactionPath) :
|
||||
_session(session),
|
||||
_space(XR_NULL_HANDLE)
|
||||
{
|
||||
// Attempt to create an action space for this pose action
|
||||
XrActionSpaceCreateInfo createInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
|
||||
createInfo.action = action->getXrAction();
|
||||
createInfo.subactionPath = subactionPath.getXrPath();
|
||||
createInfo.poseInActionSpace = poseIdentity;
|
||||
|
||||
check(xrCreateActionSpace(session->getXrSession(), &createInfo, &_space),
|
||||
"Failed to create OpenXR action space");
|
||||
}
|
||||
|
||||
Space::~Space()
|
||||
{
|
||||
if (_session.valid() && _session->valid() && valid())
|
||||
{
|
||||
check(xrDestroySpace(_space),
|
||||
"Failed to destroy OpenXR space");
|
||||
}
|
||||
}
|
||||
|
||||
Space::Location::Location() :
|
||||
_flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
Space::Location::Location(XrSpaceLocationFlags flags,
|
||||
const osg::Quat &orientation,
|
||||
const osg::Vec3f &position) :
|
||||
_flags(flags),
|
||||
_orientation(orientation),
|
||||
_position(position)
|
||||
{
|
||||
}
|
||||
|
||||
bool Space::locate(const Space *baseSpace, XrTime time,
|
||||
Space::Location &location)
|
||||
{
|
||||
if (!_session.valid() || !valid())
|
||||
return false;
|
||||
assert(_session == baseSpace->_session);
|
||||
|
||||
XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION };
|
||||
bool ret = check(xrLocateSpace(getXrSpace(),
|
||||
baseSpace->getXrSpace(),
|
||||
time,
|
||||
&spaceLocation),
|
||||
"Failed to locate OpenXR space");
|
||||
if (ret)
|
||||
{
|
||||
osg::Quat orientation(spaceLocation.pose.orientation.x,
|
||||
spaceLocation.pose.orientation.y,
|
||||
spaceLocation.pose.orientation.z,
|
||||
spaceLocation.pose.orientation.w);
|
||||
osg::Vec3f position(spaceLocation.pose.position.x,
|
||||
spaceLocation.pose.position.y,
|
||||
spaceLocation.pose.position.z);
|
||||
location = Location(spaceLocation.locationFlags,
|
||||
orientation,
|
||||
position);
|
||||
}
|
||||
else
|
||||
{
|
||||
location = Location();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SPACE
|
||||
#define OSGXR_OPENXR_SPACE 1
|
||||
|
||||
#include "Action.h"
|
||||
#include "Path.h"
|
||||
#include "Session.h"
|
||||
|
||||
#include <osg/Quat>
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Space : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
/// Create a reference space
|
||||
Space(Session *session, XrReferenceSpaceType type);
|
||||
/// Create an action space
|
||||
Space(Session *session, ActionPose *action,
|
||||
Path subactionPath = Path());
|
||||
virtual ~Space();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _space != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _session->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline XrSpace getXrSpace() const
|
||||
{
|
||||
return _space;
|
||||
}
|
||||
|
||||
// Locating a space
|
||||
|
||||
class Location
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Location();
|
||||
Location(XrSpaceLocationFlags flags,
|
||||
const osg::Quat &orientation,
|
||||
const osg::Vec3f &position);
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _flags != 0;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
bool isOrientationValid() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT;
|
||||
}
|
||||
|
||||
bool isPositionValid() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
}
|
||||
|
||||
bool isOrientationTracked() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT;
|
||||
}
|
||||
|
||||
bool isPositionTracked() const
|
||||
{
|
||||
return _flags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT;
|
||||
}
|
||||
|
||||
XrSpaceLocationFlags getFlags() const
|
||||
{
|
||||
return _flags;
|
||||
}
|
||||
|
||||
const osg::Quat &getOrientation() const
|
||||
{
|
||||
return _orientation;
|
||||
}
|
||||
|
||||
const osg::Vec3f &getPosition() const
|
||||
{
|
||||
return _position;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
XrSpaceLocationFlags _flags;
|
||||
osg::Quat _orientation;
|
||||
osg::Vec3f _position;
|
||||
};
|
||||
|
||||
bool locate(const Space *baseSpace, XrTime time,
|
||||
Location &location);
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<Session> _session;
|
||||
XrSpace _space;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "Swapchain.h"
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
Swapchain::Swapchain(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format) :
|
||||
_session(session),
|
||||
_swapchain(XR_NULL_HANDLE),
|
||||
_width(view.getRecommendedWidth()),
|
||||
_height(view.getRecommendedHeight()),
|
||||
_samples(view.getRecommendedSamples()),
|
||||
_format(format),
|
||||
_readImageTextures(false)
|
||||
{
|
||||
XrSwapchainCreateInfo createInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
|
||||
createInfo.usageFlags = usageFlags;
|
||||
createInfo.format = format;
|
||||
createInfo.sampleCount = _samples;
|
||||
createInfo.width = _width;
|
||||
createInfo.height = _height;
|
||||
createInfo.faceCount = 1;
|
||||
createInfo.arraySize = 1;
|
||||
createInfo.mipCount = 1;
|
||||
|
||||
bool switchContext = _session->shouldSwitchContext();
|
||||
auto restoreAction = _session->getRestoreAction();
|
||||
if (switchContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
check(xrCreateSwapchain(getXrSession(), &createInfo, &_swapchain),
|
||||
"Failed to create OpenXR swapchain");
|
||||
|
||||
if (restoreAction == Session::CONTEXT_RESTORE)
|
||||
_session->makeCurrent();
|
||||
else if (switchContext || restoreAction == Session::CONTEXT_RELEASE)
|
||||
_session->releaseContext();
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain()
|
||||
{
|
||||
if (_session->valid() && valid())
|
||||
{
|
||||
// GL context must not be bound in another thread
|
||||
check(xrDestroySwapchain(_swapchain),
|
||||
"Failed to destroy OpenXR swapchain");
|
||||
}
|
||||
}
|
||||
|
||||
const Swapchain::ImageTextures &Swapchain::getImageTextures() const
|
||||
{
|
||||
if (!_readImageTextures)
|
||||
{
|
||||
// Enumerate the images
|
||||
uint32_t imageCount;
|
||||
// GL context must not be bound in another thread
|
||||
if (check(xrEnumerateSwapchainImages(_swapchain, 0, &imageCount, nullptr),
|
||||
"Failed to count OpenXR swapchain images"))
|
||||
{
|
||||
if (imageCount)
|
||||
{
|
||||
std::vector<XrSwapchainImageOpenGLKHR> images(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
|
||||
if (check(xrEnumerateSwapchainImages(_swapchain, images.size(), &imageCount,
|
||||
(XrSwapchainImageBaseHeader *)images.data()),
|
||||
"Failed to enumerate OpenXR swapchain images"))
|
||||
{
|
||||
for (auto image: images)
|
||||
_imageTextures.push_back(image.image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readImageTextures = true;
|
||||
}
|
||||
|
||||
return _imageTextures;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> Swapchain::getImageOsgTexture(unsigned int index) const
|
||||
{
|
||||
if (_imageOsgTextures.empty())
|
||||
{
|
||||
getImageTextures();
|
||||
_imageOsgTextures.resize(_imageTextures.size());
|
||||
}
|
||||
|
||||
assert(index < _imageOsgTextures.size());
|
||||
if (!_imageOsgTextures[index].valid())
|
||||
{
|
||||
// Create an OSG texture out of it
|
||||
osg::Texture2D *texture = new osg::Texture2D;
|
||||
texture->setTextureSize(getWidth(),
|
||||
getHeight());
|
||||
texture->setInternalFormat(getFormat());
|
||||
unsigned int contextID = _session->getWindow()->getState()->getContextID();
|
||||
texture->setTextureObject(contextID, new osg::Texture::TextureObject(texture, _imageTextures[index], GL_TEXTURE_2D));
|
||||
|
||||
_imageOsgTextures[index] = texture;
|
||||
}
|
||||
|
||||
return _imageOsgTextures[index];
|
||||
}
|
||||
|
||||
int Swapchain::acquireImage() const
|
||||
{
|
||||
// Acquire a swapchain image
|
||||
uint32_t imageIndex;
|
||||
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
// GL context must not be bound in another thread
|
||||
if (check(xrAcquireSwapchainImage(_swapchain, nullptr, &imageIndex),
|
||||
"Failed to acquire swapchain image"))
|
||||
{
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return imageIndex;
|
||||
}
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Swapchain::waitImage(XrDuration timeoutNs) const
|
||||
{
|
||||
// Wait on the swapchain image
|
||||
XrSwapchainImageWaitInfo waitInfo = { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
|
||||
waitInfo.timeout = timeoutNs; // 100ms
|
||||
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
// GL context must not be bound in another thread
|
||||
bool ret = check(xrWaitSwapchainImage(_swapchain, &waitInfo),
|
||||
"Failed to wait for swapchain image");
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Swapchain::releaseImage() const
|
||||
{
|
||||
// Release the swapchain image
|
||||
bool restoreContext = _session->shouldRestoreContext();
|
||||
// GL context must not be bound in another thread
|
||||
check(xrReleaseSwapchainImage(_swapchain, nullptr),
|
||||
"Failed to release OpenXR swapchain image");
|
||||
|
||||
if (restoreContext)
|
||||
_session->makeCurrent();
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SWAPCHAIN
|
||||
#define OSGXR_OPENXR_SWAPCHAIN 1
|
||||
|
||||
#include "Session.h"
|
||||
#include "System.h"
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class Swapchain : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
Swapchain(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format);
|
||||
// GL context must not be bound in another thread
|
||||
virtual ~Swapchain();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _swapchain != XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _session->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline XrSession getXrSession() const
|
||||
{
|
||||
return _session->getXrSession();
|
||||
}
|
||||
|
||||
inline XrSwapchain getXrSwapchain() const
|
||||
{
|
||||
return _swapchain;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
inline uint32_t getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
inline uint32_t getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
inline uint32_t getSamples() const
|
||||
{
|
||||
return _samples;
|
||||
}
|
||||
|
||||
inline int64_t getFormat() const
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
typedef std::vector<GLuint> ImageTextures;
|
||||
// GL context must not be bound in another thread
|
||||
const ImageTextures &getImageTextures() const;
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> getImageOsgTexture(unsigned int index) const;
|
||||
|
||||
// Operations
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
int acquireImage() const;
|
||||
// GL context must not be bound in another thread
|
||||
bool waitImage(XrDuration timeoutNs) const;
|
||||
// GL context must not be bound in another thread
|
||||
void releaseImage() const;
|
||||
|
||||
protected:
|
||||
|
||||
// Session data
|
||||
osg::ref_ptr<Session> _session;
|
||||
XrSwapchain _swapchain;
|
||||
uint32_t _width;
|
||||
uint32_t _height;
|
||||
uint32_t _samples;
|
||||
int64_t _format;
|
||||
|
||||
// Image OpenGL textures
|
||||
mutable bool _readImageTextures;
|
||||
mutable ImageTextures _imageTextures;
|
||||
mutable std::vector<osg::ref_ptr<osg::Texture2D>> _imageOsgTextures;
|
||||
|
||||
// Current image
|
||||
mutable int _currentImage;
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "SwapchainGroup.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
SwapchainGroup::SwapchainGroup(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format,
|
||||
XrSwapchainUsageFlags depthUsageFlags,
|
||||
int64_t depthFormat) :
|
||||
_swapchain(new Swapchain(session, view, usageFlags, format))
|
||||
{
|
||||
if (depthFormat)
|
||||
_depthSwapchain = new Swapchain(session, view, depthUsageFlags, depthFormat);
|
||||
}
|
||||
|
||||
SwapchainGroup::~SwapchainGroup()
|
||||
{
|
||||
}
|
||||
|
||||
int SwapchainGroup::acquireImages() const
|
||||
{
|
||||
int imageIndex = _swapchain->acquireImage();
|
||||
if (depthValid())
|
||||
{
|
||||
int depthImageIndex = _depthSwapchain->acquireImage();
|
||||
if (imageIndex != depthImageIndex)
|
||||
OSG_WARN << "Depth swapchain image mismatch, expected " << imageIndex
|
||||
<< ", got " << depthImageIndex << std::endl;
|
||||
}
|
||||
return imageIndex;
|
||||
}
|
||||
|
||||
bool SwapchainGroup::waitImages(XrDuration timeoutNs) const
|
||||
{
|
||||
bool ret = _swapchain->waitImage(timeoutNs);
|
||||
if (depthValid())
|
||||
{
|
||||
bool depthRet = _depthSwapchain->waitImage(timeoutNs);
|
||||
ret = ret && depthRet;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SwapchainGroup::releaseImages() const
|
||||
{
|
||||
_swapchain->releaseImage();
|
||||
if (depthValid())
|
||||
_depthSwapchain->releaseImage();
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SWAPCHAIN_GROUP
|
||||
#define OSGXR_OPENXR_SWAPCHAIN_GROUP 1
|
||||
|
||||
#include "Swapchain.h"
|
||||
|
||||
#include <osg/Referenced>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class SwapchainGroupSubImage;
|
||||
|
||||
/// Groups colour and depth swapchains together
|
||||
class SwapchainGroup : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SwapchainGroupSubImage SubImage;
|
||||
|
||||
// GL context must not be bound in another thread
|
||||
SwapchainGroup(osg::ref_ptr<Session> session,
|
||||
const System::ViewConfiguration::View &view,
|
||||
XrSwapchainUsageFlags usageFlags,
|
||||
int64_t format,
|
||||
XrSwapchainUsageFlags depthUsageFlags = 0,
|
||||
int64_t depthFormat = 0);
|
||||
// GL context must not be bound in another thread
|
||||
virtual ~SwapchainGroup();
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _swapchain->valid();
|
||||
}
|
||||
|
||||
inline bool depthValid() const
|
||||
{
|
||||
return _depthSwapchain.valid() && _depthSwapchain->valid();
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
inline osg::ref_ptr<Swapchain> getSwapchain() const
|
||||
{
|
||||
return _swapchain;
|
||||
}
|
||||
|
||||
inline osg::ref_ptr<Swapchain> getDepthSwapchain() const
|
||||
{
|
||||
return _depthSwapchain;
|
||||
}
|
||||
|
||||
inline XrSwapchain getXrSwapchain() const
|
||||
{
|
||||
return _swapchain->getXrSwapchain();
|
||||
}
|
||||
|
||||
inline XrSwapchain getDepthXrSwapchain() const
|
||||
{
|
||||
if (_depthSwapchain.valid())
|
||||
return _depthSwapchain->getXrSwapchain();
|
||||
else
|
||||
return XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline uint32_t getWidth() const
|
||||
{
|
||||
return _swapchain->getWidth();
|
||||
}
|
||||
|
||||
inline uint32_t getHeight() const
|
||||
{
|
||||
return _swapchain->getHeight();
|
||||
}
|
||||
|
||||
inline uint32_t getSamples() const
|
||||
{
|
||||
return _swapchain->getSamples();
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
typedef Swapchain::ImageTextures ImageTextures;
|
||||
const ImageTextures &getImageTextures() const
|
||||
{
|
||||
return _swapchain->getImageTextures();
|
||||
}
|
||||
const ImageTextures &getDepthImageTextures() const
|
||||
{
|
||||
return _depthSwapchain->getImageTextures();
|
||||
}
|
||||
|
||||
// Operations
|
||||
|
||||
int acquireImages() const;
|
||||
bool waitImages(XrDuration timeoutNs) const;
|
||||
void releaseImages() const;
|
||||
|
||||
protected:
|
||||
|
||||
osg::ref_ptr<Swapchain> _swapchain;
|
||||
osg::ref_ptr<Swapchain> _depthSwapchain;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SWAPCHAIN_GROUP_SUB_IMAGE
|
||||
#define OSGXR_OPENXR_SWAPCHAIN_GROUP_SUB_IMAGE 1
|
||||
|
||||
#include "SwapchainGroup.h"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class SwapchainGroupSubImage
|
||||
{
|
||||
public:
|
||||
SwapchainGroupSubImage(SwapchainGroup *group) :
|
||||
_group(group),
|
||||
_x(0),
|
||||
_y(0),
|
||||
_width(group->getWidth()),
|
||||
_height(group->getHeight()),
|
||||
_arrayIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
SwapchainGroupSubImage(SwapchainGroup *group,
|
||||
const System::ViewConfiguration::View::Viewport &vp) :
|
||||
_group(group),
|
||||
_x(vp.x),
|
||||
_y(vp.y),
|
||||
_width(vp.width),
|
||||
_height(vp.height),
|
||||
_arrayIndex(vp.arrayIndex)
|
||||
{
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return _group->valid();
|
||||
}
|
||||
|
||||
bool depthValid() const
|
||||
{
|
||||
return _group->depthValid();
|
||||
}
|
||||
|
||||
osg::ref_ptr<SwapchainGroup> getSwapchainGroup() const
|
||||
{
|
||||
return _group;
|
||||
}
|
||||
|
||||
uint32_t getX() const
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
uint32_t getY() const
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
uint32_t getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
uint32_t getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
uint32_t getArrayIndex() const
|
||||
{
|
||||
return _arrayIndex;
|
||||
}
|
||||
|
||||
void getXrSubImage(XrSwapchainSubImage *out) const
|
||||
{
|
||||
out->swapchain = _group->getXrSwapchain();
|
||||
out->imageRect.offset = { (int32_t)_x,
|
||||
(int32_t)_y };
|
||||
out->imageRect.extent = { (int32_t)_width,
|
||||
(int32_t)_height };
|
||||
out->imageArrayIndex = _arrayIndex;
|
||||
}
|
||||
|
||||
void getDepthXrSubImage(XrSwapchainSubImage *out) const
|
||||
{
|
||||
out->swapchain = _group->getDepthXrSwapchain();
|
||||
out->imageRect.offset = { (int32_t)_x,
|
||||
(int32_t)_y };
|
||||
out->imageRect.extent = { (int32_t)_width,
|
||||
(int32_t)_height };
|
||||
out->imageArrayIndex = _arrayIndex;
|
||||
}
|
||||
|
||||
protected:
|
||||
osg::ref_ptr<SwapchainGroup> _group;
|
||||
uint32_t _x;
|
||||
uint32_t _y;
|
||||
uint32_t _width;
|
||||
uint32_t _height;
|
||||
uint32_t _arrayIndex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "System.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace osgXR::OpenXR;
|
||||
|
||||
void System::getProperties() const
|
||||
{
|
||||
XrSystemProperties properties;
|
||||
properties.type = XR_TYPE_SYSTEM_PROPERTIES;
|
||||
properties.next = nullptr;
|
||||
|
||||
if (check(xrGetSystemProperties(getXrInstance(), _systemId, &properties),
|
||||
"Failed to get OpenXR system properties"))
|
||||
{
|
||||
memcpy(_systemName, properties.systemName, sizeof(_systemName));
|
||||
_orientationTracking = properties.trackingProperties.orientationTracking;
|
||||
_positionTracking = properties.trackingProperties.positionTracking;
|
||||
}
|
||||
|
||||
_readProperties = true;
|
||||
}
|
||||
|
||||
const System::ViewConfiguration::Views &System::ViewConfiguration::getViews() const
|
||||
{
|
||||
if (!_readViews)
|
||||
{
|
||||
uint32_t viewCount = 0;
|
||||
if (check(xrEnumerateViewConfigurationViews(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
0, &viewCount, nullptr),
|
||||
"Failed to count OpenXR view configuration views"))
|
||||
{
|
||||
if (viewCount)
|
||||
{
|
||||
std::vector<XrViewConfigurationView> views(viewCount,
|
||||
{ XR_TYPE_VIEW_CONFIGURATION_VIEW });
|
||||
if (check(xrEnumerateViewConfigurationViews(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
views.size(), &viewCount,
|
||||
views.data()),
|
||||
"Failed to enumerate OpenXR view configuration views"))
|
||||
{
|
||||
for (auto &view: views)
|
||||
_views.push_back(View(view));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readViews = true;
|
||||
}
|
||||
|
||||
return _views;
|
||||
}
|
||||
|
||||
const System::ViewConfiguration::EnvBlendModes &System::ViewConfiguration::getEnvBlendModes() const
|
||||
{
|
||||
if (!_readEnvBlendModes)
|
||||
{
|
||||
uint32_t blendModeCount = 0;
|
||||
if (check(xrEnumerateEnvironmentBlendModes(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
0, &blendModeCount, nullptr),
|
||||
"Failed to count OpenXR environment blend modes"))
|
||||
{
|
||||
if (blendModeCount)
|
||||
{
|
||||
_envBlendModes.resize(blendModeCount);
|
||||
if (!check(xrEnumerateEnvironmentBlendModes(_system->getXrInstance(),
|
||||
_system->getXrSystemId(),
|
||||
_type,
|
||||
_envBlendModes.size(),
|
||||
&blendModeCount,
|
||||
_envBlendModes.data()),
|
||||
"Failed to enumerate OpenXR environment blend modes"))
|
||||
{
|
||||
_envBlendModes.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readEnvBlendModes = true;
|
||||
}
|
||||
|
||||
return _envBlendModes;
|
||||
}
|
||||
|
||||
const System::ViewConfigurations &System::getViewConfigurations() const
|
||||
{
|
||||
if (!_readViewConfigurations)
|
||||
{
|
||||
uint32_t viewConfigCount = 0;
|
||||
if (check(xrEnumerateViewConfigurations(getXrInstance(), getXrSystemId(),
|
||||
0, &viewConfigCount, nullptr),
|
||||
"Failed to count OpenXR view configuration types"))
|
||||
{
|
||||
if (viewConfigCount)
|
||||
{
|
||||
std::vector<XrViewConfigurationType> types(viewConfigCount);
|
||||
if (check(xrEnumerateViewConfigurations(getXrInstance(), getXrSystemId(),
|
||||
types.size(), &viewConfigCount,
|
||||
types.data()),
|
||||
"Failed to enumerate OpenXR view configuration types"))
|
||||
{
|
||||
_viewConfigurations.reserve(viewConfigCount);
|
||||
for (auto type: types)
|
||||
_viewConfigurations.push_back(ViewConfiguration(this, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readViewConfigurations = true;
|
||||
}
|
||||
|
||||
return _viewConfigurations;
|
||||
}
|
||||
Vendored
+221
@@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_OPENXR_SYSTEM
|
||||
#define OSGXR_OPENXR_SYSTEM 1
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
#include <osg/DisplaySettings>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
namespace OpenXR {
|
||||
|
||||
class System
|
||||
{
|
||||
public:
|
||||
|
||||
System(Instance *instance, XrSystemId systemId) :
|
||||
_instance(instance),
|
||||
_systemId(systemId),
|
||||
_readProperties(false),
|
||||
_orientationTracking(false),
|
||||
_positionTracking(false),
|
||||
_readViewConfigurations(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Error checking
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _instance->check(result, warnMsg);
|
||||
}
|
||||
|
||||
// Conversions
|
||||
|
||||
inline Instance *getInstance()
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
inline const Instance *getInstance() const
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
inline XrInstance getXrInstance() const
|
||||
{
|
||||
return _instance->getXrInstance();
|
||||
}
|
||||
|
||||
inline XrSystemId getXrSystemId() const
|
||||
{
|
||||
return _systemId;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
void getProperties() const;
|
||||
|
||||
inline const char *getSystemName() const
|
||||
{
|
||||
if (!_readProperties)
|
||||
getProperties();
|
||||
return _systemName;
|
||||
}
|
||||
|
||||
inline bool getOrientationTracking() const
|
||||
{
|
||||
if (!_readProperties)
|
||||
getProperties();
|
||||
return _orientationTracking;
|
||||
}
|
||||
|
||||
inline bool getPositionTracking() const
|
||||
{
|
||||
if (!_readProperties)
|
||||
getProperties();
|
||||
return _positionTracking;
|
||||
}
|
||||
|
||||
class ViewConfiguration
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
ViewConfiguration(const System *system, XrViewConfigurationType type) :
|
||||
_system(system),
|
||||
_type(type),
|
||||
_readViews(false),
|
||||
_readEnvBlendModes(false)
|
||||
{
|
||||
}
|
||||
|
||||
XrViewConfigurationType getType() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
// Queries
|
||||
|
||||
class View
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
struct Viewport
|
||||
{
|
||||
uint32_t x, y, width, height, arrayIndex;
|
||||
};
|
||||
|
||||
View(uint32_t recommendedWidth,
|
||||
uint32_t recommendedHeight,
|
||||
uint32_t recommendedSamples = 1) :
|
||||
_recommendedWidth(recommendedWidth),
|
||||
_recommendedHeight(recommendedHeight),
|
||||
_recommendedSamples(recommendedSamples)
|
||||
{
|
||||
}
|
||||
|
||||
View(const XrViewConfigurationView &view) :
|
||||
_recommendedWidth(view.recommendedImageRectWidth),
|
||||
_recommendedHeight(view.recommendedImageRectHeight),
|
||||
_recommendedSamples(view.recommendedSwapchainSampleCount)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t getRecommendedWidth() const
|
||||
{
|
||||
return _recommendedWidth;
|
||||
}
|
||||
|
||||
uint32_t getRecommendedHeight() const
|
||||
{
|
||||
return _recommendedHeight;
|
||||
}
|
||||
|
||||
|
||||
uint32_t getRecommendedSamples() const
|
||||
{
|
||||
return _recommendedSamples;
|
||||
}
|
||||
|
||||
/// Tile another view horizontally after this one
|
||||
struct Viewport tileHorizontally(const View &other)
|
||||
{
|
||||
struct Viewport vp;
|
||||
vp.x = _recommendedWidth;
|
||||
vp.y = 0;
|
||||
vp.width = other._recommendedWidth;
|
||||
vp.height = other._recommendedHeight;
|
||||
vp.arrayIndex = 0;
|
||||
|
||||
_recommendedWidth += other._recommendedWidth;
|
||||
_recommendedHeight = std::max(_recommendedHeight,
|
||||
other._recommendedHeight);
|
||||
return vp;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t _recommendedWidth;
|
||||
uint32_t _recommendedHeight;
|
||||
uint32_t _recommendedSamples;
|
||||
};
|
||||
|
||||
typedef std::vector<View> Views;
|
||||
const Views &getViews() const;
|
||||
|
||||
typedef std::vector<XrEnvironmentBlendMode> EnvBlendModes;
|
||||
const EnvBlendModes &getEnvBlendModes() const;
|
||||
|
||||
protected:
|
||||
|
||||
inline bool check(XrResult result, const char *warnMsg) const
|
||||
{
|
||||
return _system->getInstance()->check(result, warnMsg);
|
||||
}
|
||||
|
||||
const System *_system;
|
||||
XrViewConfigurationType _type;
|
||||
|
||||
// Views
|
||||
mutable bool _readViews;
|
||||
mutable Views _views;
|
||||
|
||||
// Environment blend modes
|
||||
mutable bool _readEnvBlendModes;
|
||||
mutable EnvBlendModes _envBlendModes;
|
||||
};
|
||||
|
||||
typedef std::vector<ViewConfiguration> ViewConfigurations;
|
||||
const ViewConfigurations &getViewConfigurations() const;
|
||||
|
||||
protected:
|
||||
|
||||
// System data
|
||||
Instance *_instance;
|
||||
XrSystemId _systemId;
|
||||
|
||||
// Properties
|
||||
mutable char _systemName[XR_MAX_SYSTEM_NAME_SIZE];
|
||||
mutable bool _readProperties;
|
||||
mutable bool _orientationTracking;
|
||||
mutable bool _positionTracking;
|
||||
|
||||
// View configurations
|
||||
mutable bool _readViewConfigurations;
|
||||
mutable ViewConfigurations _viewConfigurations;
|
||||
|
||||
};
|
||||
|
||||
} // osgXR::OpenXR
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/OpenXRDisplay>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgViewer/ViewerBase>
|
||||
|
||||
#include "XRState.h"
|
||||
#include "XRRealizeOperation.h"
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
OpenXRDisplay::OpenXRDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
OpenXRDisplay::OpenXRDisplay(Settings *settings):
|
||||
_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
OpenXRDisplay::OpenXRDisplay(const OpenXRDisplay& rhs,
|
||||
const osg::CopyOp& copyop):
|
||||
ViewConfig(rhs,copyop),
|
||||
_settings(rhs._settings)
|
||||
{
|
||||
}
|
||||
|
||||
OpenXRDisplay::~OpenXRDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
void OpenXRDisplay::configure(osgViewer::View &view) const
|
||||
{
|
||||
osgViewer::ViewerBase *viewer = dynamic_cast<osgViewer::ViewerBase *>(&view);
|
||||
if (!viewer)
|
||||
return;
|
||||
|
||||
_state = new XRState(_settings);
|
||||
viewer->setRealizeOperation(new XRRealizeOperation(_state, &view));
|
||||
}
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/Settings>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#include "OpenXR/Instance.h"
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
Settings::Settings() :
|
||||
_appName("osgXR"),
|
||||
_appVersion(1),
|
||||
_validationLayer(false),
|
||||
_depthInfo(false),
|
||||
_visibilityMask(true),
|
||||
_formFactor(HEAD_MOUNTED_DISPLAY),
|
||||
_preferredEnvBlendModeMask(0),
|
||||
_allowedEnvBlendModeMask(0),
|
||||
_vrMode(VRMODE_AUTOMATIC),
|
||||
_swapchainMode(SWAPCHAIN_AUTOMATIC),
|
||||
_preferredRGBEncodingMask(0),
|
||||
_allowedRGBEncodingMask(0),
|
||||
_preferredDepthEncodingMask(0),
|
||||
_allowedDepthEncodingMask(0),
|
||||
_rgbBits(-1),
|
||||
_alphaBits(-1),
|
||||
_depthBits(-1),
|
||||
_stencilBits(-1),
|
||||
_unitsPerMeter(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
Settings::~Settings()
|
||||
{
|
||||
}
|
||||
|
||||
Settings *Settings::instance()
|
||||
{
|
||||
static osg::ref_ptr<Settings> settings = new Settings();
|
||||
return settings;
|
||||
}
|
||||
|
||||
unsigned int Settings::_diff(const Settings &other) const
|
||||
{
|
||||
unsigned int ret = DIFF_NONE;
|
||||
if (_appName != other._appName ||
|
||||
_appVersion != other._appVersion)
|
||||
ret |= DIFF_APP_INFO;
|
||||
if (_validationLayer != other._validationLayer)
|
||||
ret |= DIFF_VALIDATION_LAYER;
|
||||
if (_depthInfo != other._depthInfo)
|
||||
ret |= DIFF_DEPTH_INFO;
|
||||
if (_visibilityMask != other._visibilityMask)
|
||||
ret |= DIFF_VISIBILITY_MASK;
|
||||
if (_formFactor != other._formFactor)
|
||||
ret |= DIFF_FORM_FACTOR;
|
||||
if (_preferredEnvBlendModeMask != other._preferredEnvBlendModeMask ||
|
||||
_allowedEnvBlendModeMask != other._allowedEnvBlendModeMask)
|
||||
ret |= DIFF_BLEND_MODE;
|
||||
if (_vrMode != other._vrMode)
|
||||
ret |= DIFF_VR_MODE;
|
||||
if (_swapchainMode != other._swapchainMode)
|
||||
ret |= DIFF_SWAPCHAIN_MODE;
|
||||
if (_preferredRGBEncodingMask != other._preferredRGBEncodingMask ||
|
||||
_allowedRGBEncodingMask != other._allowedRGBEncodingMask)
|
||||
ret |= DIFF_RGB_ENCODING;
|
||||
if (_preferredDepthEncodingMask != other._preferredDepthEncodingMask ||
|
||||
_allowedDepthEncodingMask != other._allowedDepthEncodingMask)
|
||||
ret |= DIFF_DEPTH_ENCODING;
|
||||
if (_rgbBits != other._rgbBits)
|
||||
ret |= DIFF_RGB_BITS;
|
||||
if (_alphaBits != other._alphaBits)
|
||||
ret |= DIFF_ALPHA_BITS;
|
||||
if (_depthBits != other._depthBits)
|
||||
ret |= DIFF_DEPTH_BITS;
|
||||
if (_stencilBits != other._stencilBits)
|
||||
ret |= DIFF_STENCIL_BITS;
|
||||
if (_mirrorSettings != other._mirrorSettings)
|
||||
ret |= DIFF_MIRROR;
|
||||
if (_unitsPerMeter != other._unitsPerMeter)
|
||||
ret |= DIFF_SCALE;
|
||||
return ret;
|
||||
}
|
||||
Vendored
+109
@@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Subaction.h"
|
||||
#include "XRState.h"
|
||||
|
||||
#include <osgXR/Manager>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
// Internal API
|
||||
|
||||
Subaction::Private::Private(XRState *state,
|
||||
const std::string &path) :
|
||||
_state(state),
|
||||
_pathString(path)
|
||||
{
|
||||
}
|
||||
|
||||
void Subaction::Private::registerPublic(Subaction *subaction)
|
||||
{
|
||||
_publics.insert(subaction);
|
||||
}
|
||||
|
||||
void Subaction::Private::unregisterPublic(Subaction *subaction)
|
||||
{
|
||||
_publics.erase(subaction);
|
||||
}
|
||||
|
||||
InteractionProfile *Subaction::Private::getCurrentProfile()
|
||||
{
|
||||
if (!_currentProfile.valid())
|
||||
{
|
||||
if (_path.valid())
|
||||
_currentProfile = _state->getCurrentInteractionProfile(_path);
|
||||
}
|
||||
|
||||
return _currentProfile.get();
|
||||
}
|
||||
|
||||
void Subaction::Private::onInteractionProfileChanged(OpenXR::Session *session)
|
||||
{
|
||||
// Ensure path is set up
|
||||
setup(session->getInstance());
|
||||
|
||||
// Find whether this subaction's current interaction profile has changed
|
||||
InteractionProfile *prevProfile = _currentProfile.get();
|
||||
_currentProfile = nullptr;
|
||||
InteractionProfile *newProfile = getCurrentProfile();
|
||||
if (newProfile != prevProfile)
|
||||
{
|
||||
// Notify any derived Subaction classes from the app
|
||||
for (auto *pub: _publics)
|
||||
pub->onProfileChanged(newProfile);
|
||||
}
|
||||
}
|
||||
|
||||
const OpenXR::Path &Subaction::Private::setup(OpenXR::Instance *instance)
|
||||
{
|
||||
if (!_path.valid())
|
||||
_path = OpenXR::Path(instance, _pathString);
|
||||
return _path;
|
||||
}
|
||||
|
||||
void Subaction::Private::cleanupSession()
|
||||
{
|
||||
bool hadProfile = _currentProfile.valid();
|
||||
_currentProfile = nullptr;
|
||||
if (hadProfile)
|
||||
{
|
||||
// Notify any derived Subaction classes from the app
|
||||
for (auto *pub: _publics)
|
||||
pub->onProfileChanged(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Subaction::Private::cleanupInstance()
|
||||
{
|
||||
_path = OpenXR::Path();
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
Subaction::Subaction(Manager *manager,
|
||||
const std::string &path) :
|
||||
_private(manager->_getXrState()->getSubaction(path))
|
||||
{
|
||||
_private->registerPublic(this);
|
||||
}
|
||||
|
||||
Subaction::~Subaction()
|
||||
{
|
||||
_private->unregisterPublic(this);
|
||||
}
|
||||
|
||||
const std::string &Subaction::getPath() const
|
||||
{
|
||||
return _private->getPathString();
|
||||
}
|
||||
|
||||
InteractionProfile *Subaction::getCurrentProfile()
|
||||
{
|
||||
return _private->getCurrentProfile();
|
||||
}
|
||||
|
||||
void Subaction::onProfileChanged(InteractionProfile *newProfile)
|
||||
{
|
||||
// This is for derived classes to implement to their own ends
|
||||
}
|
||||
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_SUBACTION
|
||||
#define OSGXR_SUBACTION 1
|
||||
|
||||
#include <osgXR/Subaction>
|
||||
|
||||
#include "OpenXR/Path.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class XRState;
|
||||
|
||||
namespace OpenXR {
|
||||
class Instance;
|
||||
};
|
||||
|
||||
class Subaction::Private
|
||||
{
|
||||
public:
|
||||
|
||||
static std::shared_ptr<Private> get(Subaction *pub)
|
||||
{
|
||||
if (pub)
|
||||
return pub->_private;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Private(XRState *state, const std::string &path);
|
||||
|
||||
// Public object registration
|
||||
void registerPublic(Subaction *subaction);
|
||||
void unregisterPublic(Subaction *subaction);
|
||||
|
||||
// Accessors
|
||||
|
||||
/// Get the subaction's path as a string.
|
||||
const std::string &getPathString() const
|
||||
{
|
||||
return _pathString;
|
||||
}
|
||||
|
||||
/// Find the current interaction profile.
|
||||
InteractionProfile *getCurrentProfile();
|
||||
|
||||
// Events
|
||||
|
||||
/// Notify that an interaction profile has changed.
|
||||
void onInteractionProfileChanged(OpenXR::Session *session);
|
||||
|
||||
/// Setup path with an OpenXR instance.
|
||||
const OpenXR::Path &setup(OpenXR::Instance *instance);
|
||||
/// Clean up current profile before an OpenXR session is destroyed.
|
||||
void cleanupSession();
|
||||
/// Clean up path before an OpenXR instance is destroyed.
|
||||
void cleanupInstance();
|
||||
|
||||
private:
|
||||
|
||||
XRState *_state;
|
||||
std::string _pathString;
|
||||
std::set<Subaction *> _publics;
|
||||
|
||||
OpenXR::Path _path;
|
||||
osg::ref_ptr<InteractionProfile> _currentProfile;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+424
@@ -0,0 +1,424 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2022 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "Swapchain.h"
|
||||
|
||||
#include "OpenXR/System.h"
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <osgViewer/Renderer>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
namespace {
|
||||
|
||||
// Draw callbacks
|
||||
|
||||
class InitialDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
InitialDrawCallback(std::shared_ptr<Swapchain::Private> &swapchain) :
|
||||
_swapchain(swapchain)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
auto swapchain = _swapchain.lock();
|
||||
if (swapchain)
|
||||
swapchain->initialDrawCallback(renderInfo);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::weak_ptr<Swapchain::Private> _swapchain;
|
||||
};
|
||||
|
||||
class PreDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
PreDrawCallback(std::shared_ptr<Swapchain::Private> &swapchain) :
|
||||
_swapchain(swapchain)
|
||||
{
|
||||
swapchain->incNumDrawPasses();
|
||||
}
|
||||
|
||||
~PreDrawCallback()
|
||||
{
|
||||
auto swapchain = _swapchain.lock();
|
||||
if (swapchain)
|
||||
swapchain->decNumDrawPasses();
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
auto swapchain = _swapchain.lock();
|
||||
if (swapchain)
|
||||
swapchain->preDrawCallback(renderInfo);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::weak_ptr<Swapchain::Private> _swapchain;
|
||||
};
|
||||
|
||||
class PostDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
PostDrawCallback(std::shared_ptr<Swapchain::Private> &swapchain) :
|
||||
_swapchain(swapchain)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
auto swapchain = _swapchain.lock();
|
||||
if (swapchain)
|
||||
swapchain->postDrawCallback(renderInfo);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::weak_ptr<Swapchain::Private> _swapchain;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Internal API
|
||||
|
||||
Swapchain::Private::Private(uint32_t width, uint32_t height) :
|
||||
_preferredRGBEncodingMask(0),
|
||||
_allowedRGBEncodingMask(0),
|
||||
_rgbBits(10), // 8-bits is unlikely to be sufficient for linear encodings
|
||||
_alphaBits(0), // Alpha channel not required by default
|
||||
_width(width),
|
||||
_height(height),
|
||||
_forcedAlpha(-1.0f),
|
||||
_numDrawPasses(0),
|
||||
_updated(true)
|
||||
{
|
||||
}
|
||||
|
||||
Swapchain::Private::~Private()
|
||||
{
|
||||
}
|
||||
|
||||
void Swapchain::Private::attachToCamera(std::shared_ptr<Private> &self,
|
||||
osg::Camera *camera)
|
||||
{
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
|
||||
camera->setInitialDrawCallback(new InitialDrawCallback(self));
|
||||
camera->setPreDrawCallback(new PreDrawCallback(self));
|
||||
camera->setFinalDrawCallback(new PostDrawCallback(self));
|
||||
|
||||
// FIXME Do all cameras inherit the main display settings... eeek!
|
||||
//camera->setDisplaySettings(osg::DisplaySettings::instance());
|
||||
|
||||
if (_swapchain.valid())
|
||||
_swapchain->incNumDrawPasses(1); // FIXME HACK depends on where node is!
|
||||
}
|
||||
|
||||
void Swapchain::Private::attachToMirror(std::shared_ptr<Private> &self,
|
||||
osg::StateSet *stateSet)
|
||||
{
|
||||
_stateSets.push_back(stateSet);
|
||||
}
|
||||
|
||||
void Swapchain::Private::preferRGBEncoding(Encoding encoding)
|
||||
{
|
||||
uint32_t mask = (1u << (unsigned int)encoding);
|
||||
_preferredRGBEncodingMask |= mask;
|
||||
_allowedRGBEncodingMask |= mask;
|
||||
}
|
||||
|
||||
void Swapchain::Private::allowRGBEncoding(Encoding encoding)
|
||||
{
|
||||
uint32_t mask = (1u << (unsigned int)encoding);
|
||||
_allowedRGBEncodingMask |= mask;
|
||||
}
|
||||
|
||||
void Swapchain::Private::setRGBBits(unsigned int rgbBits)
|
||||
{
|
||||
_rgbBits = rgbBits;
|
||||
}
|
||||
|
||||
unsigned int Swapchain::Private::getRGBBits() const
|
||||
{
|
||||
return _rgbBits;
|
||||
}
|
||||
|
||||
void Swapchain::Private::setAlphaBits(unsigned int alphaBits)
|
||||
{
|
||||
_alphaBits = alphaBits;
|
||||
}
|
||||
|
||||
unsigned int Swapchain::Private::getAlphaBits() const
|
||||
{
|
||||
return _alphaBits;
|
||||
}
|
||||
|
||||
void Swapchain::Private::setWidth(uint32_t width)
|
||||
{
|
||||
if (width != _width)
|
||||
_updated = true;
|
||||
_width = width;
|
||||
}
|
||||
|
||||
uint32_t Swapchain::Private::getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
void Swapchain::Private::setHeight(uint32_t height)
|
||||
{
|
||||
if (height != _height)
|
||||
_updated = true;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
uint32_t Swapchain::Private::getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
void Swapchain::Private::setForcedAlpha(float alpha)
|
||||
{
|
||||
if (alpha < 0.0f)
|
||||
alpha = 0.0f;
|
||||
if (alpha > 1.0f)
|
||||
alpha = 1.0f;
|
||||
_forcedAlpha = alpha;
|
||||
}
|
||||
|
||||
void Swapchain::Private::disableForcedAlpha()
|
||||
{
|
||||
_forcedAlpha = -1.0f;
|
||||
}
|
||||
|
||||
float Swapchain::Private::getForcedAlpha() const
|
||||
{
|
||||
return _forcedAlpha;
|
||||
}
|
||||
|
||||
bool Swapchain::Private::setup(XRState *state, OpenXR::Session *session)
|
||||
{
|
||||
XRState *oldState = _state.get();
|
||||
if (oldState)
|
||||
{
|
||||
if (oldState != state)
|
||||
{
|
||||
OSG_WARN << "Swapchain XRState conflict" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!_updated)
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenXR::System::ViewConfiguration::View view(_width, _height);
|
||||
int64_t rgbaFormat = state->chooseRGBAFormat(_rgbBits, _alphaBits,
|
||||
_preferredRGBEncodingMask,
|
||||
_allowedRGBEncodingMask);
|
||||
if (!rgbaFormat)
|
||||
{
|
||||
std::stringstream formats;
|
||||
formats << std::hex;
|
||||
for (int64_t format: session->getSwapchainFormats())
|
||||
formats << " 0x" << format;
|
||||
OSG_WARN << "Swapchain setup: No supported swapchain format found in ["
|
||||
<< formats.str() << " ]" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
_state = state;
|
||||
_updated = false;
|
||||
_session = session;
|
||||
_swapchain = new XRState::XRSwapchain(state, session,
|
||||
view, rgbaFormat,
|
||||
0, GL_DEPTH_COMPONENT16);
|
||||
_swapchain->setForcedAlpha(_forcedAlpha);
|
||||
_swapchain->incNumDrawPasses(_numDrawPasses);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Swapchain::Private::sync()
|
||||
{
|
||||
if (_updated && _session.valid())
|
||||
return setup(_state.get(), _session.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Swapchain::Private::cleanupSession()
|
||||
{
|
||||
_swapchain = nullptr;
|
||||
_session = nullptr;
|
||||
_state = nullptr;
|
||||
}
|
||||
|
||||
bool Swapchain::Private::valid() const
|
||||
{
|
||||
return _swapchain.valid();
|
||||
}
|
||||
|
||||
void Swapchain::Private::initialDrawCallback(osg::RenderInfo &renderInfo)
|
||||
{
|
||||
if (_swapchain.valid())
|
||||
{
|
||||
// FIXME this isn't the ideal place, but it'll probably do in practice
|
||||
// due to lack of concurrency
|
||||
sync();
|
||||
osg::GraphicsOperation *graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
|
||||
osgViewer::Renderer *renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
|
||||
if (renderer != nullptr)
|
||||
{
|
||||
// Disable normal OSG FBO camera setup because it will undo the MSAA FBO configuration.
|
||||
renderer->setCameraRequiresSetUp(false);
|
||||
}
|
||||
|
||||
_state->startRendering(renderInfo.getState()->getFrameStamp());
|
||||
}
|
||||
}
|
||||
|
||||
void Swapchain::Private::preDrawCallback(osg::RenderInfo &renderInfo)
|
||||
{
|
||||
if (_swapchain.valid())
|
||||
_swapchain->preDrawCallback(renderInfo);
|
||||
}
|
||||
|
||||
void Swapchain::Private::postDrawCallback(osg::RenderInfo &renderInfo)
|
||||
{
|
||||
if (_swapchain.valid())
|
||||
{
|
||||
_swapchain->setForcedAlpha(_forcedAlpha);
|
||||
_swapchain->postDrawCallback(renderInfo);
|
||||
|
||||
const osg::FrameStamp *stamp = renderInfo.getState()->getFrameStamp();
|
||||
auto texture = _swapchain->getOsgTexture(stamp);
|
||||
if (texture.valid())
|
||||
{
|
||||
for (auto it = _stateSets.begin(); it != _stateSets.end(); ++it)
|
||||
{
|
||||
osg::ref_ptr<osg::StateSet> stateSet = *it;
|
||||
if (stateSet.valid())
|
||||
// update state set's texture object
|
||||
stateSet->setTextureAttributeAndModes(0, texture);
|
||||
else
|
||||
// clean up after stale state sets
|
||||
it = _stateSets.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME somewhere we should remove or reset the texture:
|
||||
// stateSet->removeTextureAttribute(0, osg::StateAttribute::Type::TEXTURE);
|
||||
}
|
||||
}
|
||||
|
||||
OpenXR::SwapchainGroupSubImage Swapchain::Private::convertSubImage(const SubImage &subImage) const
|
||||
{
|
||||
OpenXR::System::ViewConfiguration::View::Viewport vp = {};
|
||||
vp.x = subImage.getX();
|
||||
vp.y = subImage.getY();
|
||||
vp.width = subImage.getWidth();
|
||||
if (!vp.width)
|
||||
vp.width = _width;
|
||||
vp.height = subImage.getHeight();
|
||||
if (!vp.height)
|
||||
vp.height = _height;
|
||||
return OpenXR::SwapchainGroupSubImage(_swapchain.get(), vp);
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
Swapchain::Swapchain(uint32_t width, uint32_t height) :
|
||||
_private(new Private(width, height))
|
||||
{
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain()
|
||||
{
|
||||
}
|
||||
|
||||
void Swapchain::attachToCamera(osg::Camera *camera)
|
||||
{
|
||||
_private->attachToCamera(_private, camera);
|
||||
}
|
||||
|
||||
void Swapchain::attachToMirror(osg::StateSet *stateSet)
|
||||
{
|
||||
_private->attachToMirror(_private, stateSet);
|
||||
}
|
||||
|
||||
void Swapchain::preferRGBEncoding(Swapchain::Encoding encoding)
|
||||
{
|
||||
_private->preferRGBEncoding(encoding);
|
||||
}
|
||||
|
||||
void Swapchain::allowRGBEncoding(Swapchain::Encoding encoding)
|
||||
{
|
||||
_private->allowRGBEncoding(encoding);
|
||||
}
|
||||
|
||||
void Swapchain::setRGBBits(unsigned int rgbBits)
|
||||
{
|
||||
_private->setRGBBits(rgbBits);
|
||||
}
|
||||
|
||||
unsigned int Swapchain::getRGBBits() const
|
||||
{
|
||||
return _private->getRGBBits();
|
||||
}
|
||||
|
||||
void Swapchain::setAlphaBits(unsigned int alphaBits)
|
||||
{
|
||||
_private->setAlphaBits(alphaBits);
|
||||
}
|
||||
|
||||
unsigned int Swapchain::getAlphaBits() const
|
||||
{
|
||||
return _private->getAlphaBits();
|
||||
}
|
||||
|
||||
void Swapchain::setSize(uint32_t width, uint32_t height)
|
||||
{
|
||||
_private->setWidth(width);
|
||||
_private->setHeight(height);
|
||||
}
|
||||
|
||||
void Swapchain::setWidth(uint32_t width)
|
||||
{
|
||||
_private->setWidth(width);
|
||||
}
|
||||
|
||||
uint32_t Swapchain::getWidth() const
|
||||
{
|
||||
return _private->getWidth();
|
||||
}
|
||||
|
||||
void Swapchain::setHeight(uint32_t height)
|
||||
{
|
||||
_private->setHeight(height);
|
||||
}
|
||||
|
||||
uint32_t Swapchain::getHeight() const
|
||||
{
|
||||
return _private->getHeight();
|
||||
}
|
||||
|
||||
void Swapchain::setForcedAlpha(float alpha)
|
||||
{
|
||||
_private->setForcedAlpha(alpha);
|
||||
}
|
||||
|
||||
void Swapchain::disableForcedAlpha()
|
||||
{
|
||||
_private->disableForcedAlpha();
|
||||
}
|
||||
|
||||
float Swapchain::getForcedAlpha() const
|
||||
{
|
||||
return _private->getForcedAlpha();
|
||||
}
|
||||
Vendored
+115
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2022 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_SWAPCHAIN
|
||||
#define OSGXR_SWAPCHAIN 1
|
||||
|
||||
#include <osgXR/SubImage>
|
||||
#include <osgXR/Swapchain>
|
||||
|
||||
#include "OpenXR/Session.h"
|
||||
#include "OpenXR/SwapchainGroupSubImage.h"
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class XRState;
|
||||
|
||||
class Swapchain::Private
|
||||
{
|
||||
public:
|
||||
|
||||
static Private *get(Swapchain *pub)
|
||||
{
|
||||
return pub->_private.get();
|
||||
}
|
||||
|
||||
Private(uint32_t width, uint32_t height);
|
||||
virtual ~Private();
|
||||
|
||||
void attachToCamera(std::shared_ptr<Private> &self,
|
||||
osg::Camera *camera);
|
||||
void attachToMirror(std::shared_ptr<Private> &self,
|
||||
osg::StateSet *stateSet);
|
||||
|
||||
// Accessors
|
||||
|
||||
void preferRGBEncoding(Encoding encoding);
|
||||
void allowRGBEncoding(Encoding encoding);
|
||||
void setRGBBits(unsigned int rgbBits);
|
||||
unsigned int getRGBBits() const;
|
||||
void setAlphaBits(unsigned int alphaBits);
|
||||
unsigned int getAlphaBits() const;
|
||||
|
||||
void setWidth(uint32_t width);
|
||||
uint32_t getWidth() const;
|
||||
void setHeight(uint32_t height);
|
||||
uint32_t getHeight() const;
|
||||
|
||||
void setForcedAlpha(float alpha);
|
||||
void disableForcedAlpha();
|
||||
float getForcedAlpha() const;
|
||||
|
||||
// Internal API
|
||||
|
||||
/// Setup swapchain with an OpenXR session
|
||||
bool setup(XRState *state, OpenXR::Session *session);
|
||||
|
||||
/// Synchronise any app changes, such as resizes
|
||||
bool sync();
|
||||
|
||||
/// Clean up swapchain before an OpenXR session is destroyed
|
||||
void cleanupSession();
|
||||
|
||||
/// Find whether the swapchain is valid for use.
|
||||
bool valid() const;
|
||||
|
||||
void initialDrawCallback(osg::RenderInfo &renderInfo);
|
||||
void preDrawCallback(osg::RenderInfo &renderInfo);
|
||||
void postDrawCallback(osg::RenderInfo &renderInfo);
|
||||
|
||||
void incNumDrawPasses()
|
||||
{
|
||||
++_numDrawPasses;
|
||||
}
|
||||
void decNumDrawPasses()
|
||||
{
|
||||
--_numDrawPasses;
|
||||
}
|
||||
|
||||
OpenXR::SwapchainGroupSubImage convertSubImage(const SubImage &subImage) const;
|
||||
|
||||
protected:
|
||||
|
||||
// Format requirements
|
||||
uint32_t _preferredRGBEncodingMask;
|
||||
uint32_t _allowedRGBEncodingMask;
|
||||
int _rgbBits; // for linear RGB formats, per channel
|
||||
int _alphaBits;
|
||||
|
||||
// Dimention requirements
|
||||
uint32_t _width;
|
||||
uint32_t _height;
|
||||
|
||||
// Forced alpha
|
||||
float _forcedAlpha;
|
||||
|
||||
unsigned int _numDrawPasses;
|
||||
bool _updated;
|
||||
|
||||
// State sets to update
|
||||
std::list<osg::observer_ptr<osg::StateSet>> _stateSets;
|
||||
|
||||
// State
|
||||
osg::observer_ptr<XRState> _state;
|
||||
osg::observer_ptr<OpenXR::Session> _session;
|
||||
osg::ref_ptr<XRState::XRSwapchain> _swapchain;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_Version
|
||||
#define OSGXR_Version 1
|
||||
|
||||
#define OSGXR_MAJOR_VERSION @osgXR_MAJOR_VERSION@
|
||||
#define OSGXR_MINOR_VERSION @osgXR_MINOR_VERSION@
|
||||
#define OSGXR_PATCH_VERSION @osgXR_PATCH_VERSION@
|
||||
#define OSGXR_SOVERSION @osgXR_SOVERSION@
|
||||
|
||||
#endif
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/View>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
View::View(osgViewer::GraphicsWindow *window, osgViewer::View *osgView) :
|
||||
_window(window),
|
||||
_osgView(osgView)
|
||||
{
|
||||
}
|
||||
|
||||
View::~View()
|
||||
{
|
||||
}
|
||||
Vendored
+151
@@ -0,0 +1,151 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "XRFramebuffer.h"
|
||||
|
||||
#include <osg/FrameBufferObject>
|
||||
#include <osg/Image>
|
||||
#include <osg/State>
|
||||
#include <osg/Version>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
#if(OSG_VERSION_GREATER_OR_EQUAL(3, 4, 0))
|
||||
typedef osg::GLExtensions OSG_GLExtensions;
|
||||
#else
|
||||
typedef osg::FBOExtensions OSG_GLExtensions;
|
||||
#endif
|
||||
|
||||
static const OSG_GLExtensions* getGLExtensions(const osg::State& state)
|
||||
{
|
||||
#if(OSG_VERSION_GREATER_OR_EQUAL(3, 4, 0))
|
||||
return state.get<osg::GLExtensions>();
|
||||
#else
|
||||
return osg::FBOExtensions::instance(state.getContextID(), true);
|
||||
#endif
|
||||
}
|
||||
|
||||
XRFramebuffer::XRFramebuffer(uint32_t width, uint32_t height,
|
||||
GLuint texture, GLuint depthTexture) :
|
||||
_width(width),
|
||||
_height(height),
|
||||
_depthFormat(GL_DEPTH_COMPONENT16),
|
||||
_fbo(0),
|
||||
_texture(texture),
|
||||
_depthTexture(depthTexture),
|
||||
_generated(false),
|
||||
_boundTexture(false),
|
||||
_boundDepthTexture(false),
|
||||
_deleteDepthTexture(false)
|
||||
{
|
||||
}
|
||||
|
||||
XRFramebuffer::~XRFramebuffer()
|
||||
{
|
||||
}
|
||||
|
||||
bool XRFramebuffer::valid(osg::State &state) const
|
||||
{
|
||||
if (!_fbo)
|
||||
return false;
|
||||
|
||||
const OSG_GLExtensions *fbo_ext = getGLExtensions(state);
|
||||
GLenum complete = fbo_ext->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
|
||||
switch (complete)
|
||||
{
|
||||
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
||||
return true;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
|
||||
OSG_WARN << "FBO Incomplete attachment" << std::endl;
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
|
||||
OSG_WARN << "FBO Incomplete missing attachment" << std::endl;
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
|
||||
OSG_WARN << "FBO Incomplete draw buffer" << std::endl;
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
|
||||
OSG_WARN << "FBO Incomplete read buffer" << std::endl;
|
||||
break;
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
||||
OSG_WARN << "FBO Incomplete unsupported" << std::endl;
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
|
||||
OSG_WARN << "FBO Incomplete multisample" << std::endl;
|
||||
break;
|
||||
default:
|
||||
OSG_WARN << "FBO Incomplete ??? (0x" << std::hex << complete << std::dec << ")" << std::endl;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void XRFramebuffer::bind(osg::State &state)
|
||||
{
|
||||
const OSG_GLExtensions *fbo_ext = getGLExtensions(state);
|
||||
|
||||
if (!_fbo && !_generated)
|
||||
{
|
||||
fbo_ext->glGenFramebuffers(1, &_fbo);
|
||||
_generated = true;
|
||||
}
|
||||
|
||||
if (_fbo)
|
||||
{
|
||||
fbo_ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, _fbo);
|
||||
if (!_boundTexture && _texture)
|
||||
{
|
||||
fbo_ext->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _texture, 0);
|
||||
_boundTexture = true;
|
||||
}
|
||||
if (!_boundDepthTexture)
|
||||
{
|
||||
if (!_depthTexture)
|
||||
{
|
||||
glGenTextures(1, &_depthTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, _depthTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, _depthFormat, _width, _height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
_deleteDepthTexture = true;
|
||||
}
|
||||
|
||||
fbo_ext->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, _depthTexture, 0);
|
||||
_boundDepthTexture = true;
|
||||
|
||||
valid(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XRFramebuffer::unbind(osg::State &state)
|
||||
{
|
||||
const OSG_GLExtensions *fbo_ext = getGLExtensions(state);
|
||||
|
||||
if (_fbo && _generated)
|
||||
fbo_ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
|
||||
void XRFramebuffer::releaseGLObjects(osg::State &state)
|
||||
{
|
||||
// FIXME can we do it like RenderBuffer::releaseGLObjects?
|
||||
// FIXME better yet, switch to use FrameBufferObject, dynamically bound
|
||||
|
||||
// GL context must be current
|
||||
if (_fbo)
|
||||
{
|
||||
/*
|
||||
unsigned int contextID = state->getContextID();
|
||||
osg::get<GLFrameBufferObjectManager>(contextID)->scheduleGLObjectForDeletion(_fbo);
|
||||
*/
|
||||
const OSG_GLExtensions *fbo_ext = getGLExtensions(state);
|
||||
fbo_ext->glDeleteFramebuffers(1, &_fbo);
|
||||
_fbo = 0;
|
||||
}
|
||||
if (_deleteDepthTexture)
|
||||
{
|
||||
glDeleteTextures(1, &_depthTexture);
|
||||
_depthTexture = 0;
|
||||
_deleteDepthTexture = false;
|
||||
}
|
||||
}
|
||||
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_XRFRAMEBUFFER
|
||||
#define OSGXR_XRFRAMEBUFFER 1
|
||||
|
||||
#include <osg/GL>
|
||||
#include <osg/Referenced>
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class XRFramebuffer : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
explicit XRFramebuffer(uint32_t width, uint32_t height,
|
||||
GLuint texture, GLuint depthTexture = 0);
|
||||
// releaseGLObjects() first
|
||||
virtual ~XRFramebuffer();
|
||||
|
||||
void setDepthFormat(GLenum depthFormat)
|
||||
{
|
||||
_depthFormat = depthFormat;
|
||||
}
|
||||
|
||||
bool valid(osg::State &state) const;
|
||||
void bind(osg::State &state);
|
||||
void unbind(osg::State &state);
|
||||
// GL context must be current
|
||||
void releaseGLObjects(osg::State &state);
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t _width;
|
||||
uint32_t _height;
|
||||
GLenum _depthFormat;
|
||||
|
||||
GLuint _fbo;
|
||||
GLuint _texture;
|
||||
GLuint _depthTexture;
|
||||
|
||||
bool _generated;
|
||||
bool _boundTexture;
|
||||
bool _boundDepthTexture;
|
||||
bool _deleteDepthTexture;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include "XRRealizeOperation.h"
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
XRRealizeOperation::XRRealizeOperation(osg::ref_ptr<XRState> state,
|
||||
osgViewer::View *view) :
|
||||
osg::GraphicsOperation("XRRealizeOperation", false),
|
||||
_state(state),
|
||||
_view(view),
|
||||
_realized(false)
|
||||
{
|
||||
}
|
||||
|
||||
void XRRealizeOperation::operator () (osg::GraphicsContext *gc)
|
||||
{
|
||||
if (!_realized)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
gc->makeCurrent();
|
||||
|
||||
auto *window = dynamic_cast<osgViewer::GraphicsWindow *>(gc);
|
||||
if (window)
|
||||
{
|
||||
_state->init(window, _view);
|
||||
_realized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_XRREALIZEOPERATION
|
||||
#define OSGXR_XRREALIZEOPERATION 1
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgViewer/View>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class XRState;
|
||||
|
||||
class XRRealizeOperation : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
|
||||
explicit XRRealizeOperation(osg::ref_ptr<XRState> state,
|
||||
osgViewer::View *view);
|
||||
|
||||
void operator () (osg::GraphicsContext *gc) override;
|
||||
|
||||
bool realized() const
|
||||
{
|
||||
return _realized;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
OpenThreads::Mutex _mutex;
|
||||
osg::ref_ptr<XRState> _state;
|
||||
osgViewer::View *_view;
|
||||
bool _realized;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Vendored
+1937
File diff suppressed because it is too large
Load Diff
Vendored
+659
@@ -0,0 +1,659 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_XRSTATE
|
||||
#define OSGXR_XRSTATE 1
|
||||
|
||||
#include "OpenXR/ActionSet.h"
|
||||
#include "OpenXR/EventHandler.h"
|
||||
#include "OpenXR/Instance.h"
|
||||
#include "OpenXR/InteractionProfile.h"
|
||||
#include "OpenXR/System.h"
|
||||
#include "OpenXR/Session.h"
|
||||
#include "OpenXR/SwapchainGroup.h"
|
||||
#include "OpenXR/SwapchainGroupSubImage.h"
|
||||
#include "OpenXR/Compositor.h"
|
||||
#include "OpenXR/DepthInfo.h"
|
||||
|
||||
#include "XRFramebuffer.h"
|
||||
#include "FrameStampedVector.h"
|
||||
#include "FrameStore.h"
|
||||
|
||||
#include <osg/DisplaySettings>
|
||||
#include <osg/Referenced>
|
||||
#include <osg/observer_ptr>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <osgXR/ActionSet>
|
||||
#include <osgXR/CompositionLayer>
|
||||
#include <osgXR/InteractionProfile>
|
||||
#include <osgXR/Settings>
|
||||
#include <osgXR/Subaction>
|
||||
#include <osgXR/View>
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace osg {
|
||||
class FrameStamp;
|
||||
}
|
||||
|
||||
namespace osgViewer {
|
||||
class ViewerBase;
|
||||
}
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class Manager;
|
||||
|
||||
class XRState : public OpenXR::EventHandler
|
||||
{
|
||||
public:
|
||||
typedef Settings::VRMode VRMode;
|
||||
typedef Settings::SwapchainMode SwapchainMode;
|
||||
|
||||
XRState(Settings *settings, Manager *manager = nullptr);
|
||||
|
||||
/// Represents a swapchain group
|
||||
class XRSwapchain : public OpenXR::SwapchainGroup
|
||||
{
|
||||
public:
|
||||
|
||||
XRSwapchain(XRState *state,
|
||||
osg::ref_ptr<OpenXR::Session> session,
|
||||
const OpenXR::System::ViewConfiguration::View &view,
|
||||
int64_t chosenRGBAFormat,
|
||||
int64_t chosenDepthFormat,
|
||||
GLenum fallbackDepthFormat);
|
||||
|
||||
// GL context must be current (for XRFramebuffer)
|
||||
virtual ~XRSwapchain();
|
||||
|
||||
void setForcedAlpha(float alpha = -1.0f)
|
||||
{
|
||||
_forcedAlpha = alpha;
|
||||
}
|
||||
|
||||
void incNumDrawPasses(unsigned int num = 1)
|
||||
{
|
||||
_numDrawPasses += num;
|
||||
}
|
||||
|
||||
void decNumDrawPasses(unsigned int num = 1)
|
||||
{
|
||||
_numDrawPasses -= num;
|
||||
}
|
||||
|
||||
unsigned int getNumDrawPasses()
|
||||
{
|
||||
return _numDrawPasses;
|
||||
}
|
||||
|
||||
void setupImage(const osg::FrameStamp *stamp);
|
||||
|
||||
void preDrawCallback(osg::RenderInfo &renderInfo);
|
||||
void postDrawCallback(osg::RenderInfo &renderInfo);
|
||||
void endFrame();
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> getOsgTexture(const osg::FrameStamp *stamp);
|
||||
|
||||
protected:
|
||||
|
||||
XRState *_state;
|
||||
FrameStampedVector<osg::ref_ptr<XRFramebuffer> > _imageFramebuffers;
|
||||
|
||||
float _forcedAlpha;
|
||||
|
||||
/// Number of expected draw passes.
|
||||
unsigned int _numDrawPasses;
|
||||
unsigned int _drawPassesDone;
|
||||
bool _imagesReady;
|
||||
};
|
||||
|
||||
/// Represents an OpenXR view
|
||||
class XRView : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
XRView(XRState *state,
|
||||
uint32_t viewIndex,
|
||||
osg::ref_ptr<XRSwapchain> swapchain);
|
||||
XRView(XRState *state,
|
||||
uint32_t viewIndex,
|
||||
osg::ref_ptr<XRSwapchain> swapchain,
|
||||
const OpenXR::System::ViewConfiguration::View::Viewport &viewport);
|
||||
|
||||
// GL context must be current (for XRFramebuffer)
|
||||
virtual ~XRView();
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return _swapchainSubImage.valid();
|
||||
}
|
||||
|
||||
osg::ref_ptr<XRSwapchain> getSwapchain()
|
||||
{
|
||||
return static_cast<XRSwapchain *>(_swapchainSubImage.getSwapchainGroup().get());
|
||||
}
|
||||
|
||||
const XRSwapchain::SubImage &getSubImage() const
|
||||
{
|
||||
return _swapchainSubImage;
|
||||
}
|
||||
|
||||
void setupCamera(osg::ref_ptr<osg::Camera> camera);
|
||||
|
||||
void endFrame(OpenXR::Session::Frame *frame);
|
||||
|
||||
protected:
|
||||
|
||||
XRState *_state;
|
||||
XRSwapchain::SubImage _swapchainSubImage;
|
||||
|
||||
uint32_t _viewIndex;
|
||||
};
|
||||
|
||||
/** Represents a generic app level view.
|
||||
* This may handle multiple OpenXR views.
|
||||
*/
|
||||
class AppView : public View
|
||||
{
|
||||
public:
|
||||
|
||||
AppView(XRState *state,
|
||||
osgViewer::GraphicsWindow *window,
|
||||
osgViewer::View *osgView);
|
||||
virtual ~AppView();
|
||||
|
||||
void destroy();
|
||||
|
||||
void init();
|
||||
|
||||
protected:
|
||||
|
||||
bool _valid;
|
||||
|
||||
XRState *_state;
|
||||
};
|
||||
|
||||
/// Represents an app level view in slave cams mode
|
||||
class SlaveCamsAppView : public AppView
|
||||
{
|
||||
public:
|
||||
|
||||
SlaveCamsAppView(XRState *state,
|
||||
uint32_t viewIndex,
|
||||
osgViewer::GraphicsWindow *window,
|
||||
osgViewer::View *osgView);
|
||||
|
||||
void addSlave(osg::Camera *slaveCamera) override;
|
||||
void removeSlave(osg::Camera *slaveCamera) override;
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t _viewIndex;
|
||||
};
|
||||
|
||||
/// Represents an app level view in scene view mode
|
||||
class SceneViewAppView : public AppView
|
||||
{
|
||||
public:
|
||||
|
||||
SceneViewAppView(XRState *state,
|
||||
osgViewer::GraphicsWindow *window,
|
||||
osgViewer::View *osgView);
|
||||
|
||||
void addSlave(osg::Camera *slaveCamera) override;
|
||||
void removeSlave(osg::Camera *slaveCamera) override;
|
||||
};
|
||||
|
||||
bool hasValidationLayer() const;
|
||||
bool hasDepthInfoExtension() const;
|
||||
bool hasVisibilityMaskExtension() const;
|
||||
|
||||
inline const char *getRuntimeName() const
|
||||
{
|
||||
if (_currentState < VRSTATE_INSTANCE)
|
||||
return "";
|
||||
return _instance->getRuntimeName();
|
||||
}
|
||||
|
||||
inline const char *getSystemName() const
|
||||
{
|
||||
if (_currentState < VRSTATE_SYSTEM)
|
||||
return "";
|
||||
return _system->getSystemName();
|
||||
}
|
||||
|
||||
inline bool getPresent() const
|
||||
{
|
||||
return _instance.valid() && _instance->valid();
|
||||
}
|
||||
|
||||
inline bool valid() const
|
||||
{
|
||||
return _currentState >= VRSTATE_SESSION;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
/// No OpenXR instance.
|
||||
VRSTATE_DISABLED = 0,
|
||||
/// OpenXR instance created.
|
||||
VRSTATE_INSTANCE,
|
||||
/// Valid OpenXR system found.
|
||||
VRSTATE_SYSTEM,
|
||||
/// Session created
|
||||
VRSTATE_SESSION,
|
||||
/// Actions configured
|
||||
VRSTATE_ACTIONS,
|
||||
|
||||
VRSTATE_MAX,
|
||||
} VRState;
|
||||
|
||||
/// Set the init state to drop down to before returning to prior level.
|
||||
void setDownState(VRState downState)
|
||||
{
|
||||
if (downState < _downState && downState < _currentState)
|
||||
{
|
||||
_downState = downState;
|
||||
_stateChanged = true;
|
||||
}
|
||||
}
|
||||
/// Get the current init state to rise up to.
|
||||
VRState getUpState() const
|
||||
{
|
||||
return _upState;
|
||||
}
|
||||
/// Get the current init state to rise up to.
|
||||
VRState getCurrentState() const
|
||||
{
|
||||
return _currentState;
|
||||
}
|
||||
/// Set the init state to rise up to.
|
||||
void setUpState(VRState upState)
|
||||
{
|
||||
if (upState != _upState)
|
||||
{
|
||||
_upState = upState;
|
||||
_stateChanged = true;
|
||||
}
|
||||
}
|
||||
/// Set the minimum init state to rise up to.
|
||||
void setMinUpState(VRState minUpState)
|
||||
{
|
||||
if (minUpState > _upState)
|
||||
{
|
||||
_upState = minUpState;
|
||||
_stateChanged = true;
|
||||
}
|
||||
}
|
||||
/// Set destination state, both up and down.
|
||||
void setDestState(VRState destState)
|
||||
{
|
||||
setDownState(destState);
|
||||
setUpState(destState);
|
||||
}
|
||||
/// Find if updates are needed for state changes.
|
||||
bool isStateUpdateNeeded() const
|
||||
{
|
||||
return _currentState > _downState || _currentState < _upState;
|
||||
}
|
||||
|
||||
/// Find if a VR session is running.
|
||||
bool isRunning() const
|
||||
{
|
||||
if (_currentState < VRSTATE_SESSION)
|
||||
return false;
|
||||
return _session->isRunning();
|
||||
}
|
||||
|
||||
/// Set whether probing should be active.
|
||||
void setProbing(bool probing)
|
||||
{
|
||||
if (_probing == probing)
|
||||
return;
|
||||
_probing = probing;
|
||||
if (probing)
|
||||
{
|
||||
// Init at least up to system
|
||||
setMinUpState(VRSTATE_SYSTEM);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If only initing to system, shutdown
|
||||
if (_upState <= VRSTATE_SYSTEM)
|
||||
setDestState(VRSTATE_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
VRState getProbingState() const
|
||||
{
|
||||
if (_instance.valid() && _instance->getQuirk(OpenXR::QUIRK_AVOID_DESTROY_INSTANCE))
|
||||
return _probing ? VRSTATE_SYSTEM : VRSTATE_DISABLED;
|
||||
return VRSTATE_DISABLED;
|
||||
}
|
||||
|
||||
void setViewer(osgViewer::ViewerBase *viewer)
|
||||
{
|
||||
_viewer = viewer;
|
||||
}
|
||||
|
||||
/// Set the NodeMasks to use for visibility masks.
|
||||
void setVisibilityMaskNodeMasks(osg::Node::NodeMask left,
|
||||
osg::Node::NodeMask right)
|
||||
{
|
||||
_visibilityMaskLeft = left;
|
||||
_visibilityMaskRight = right;
|
||||
}
|
||||
|
||||
/// Get the subaction object for a subaction path string.
|
||||
std::shared_ptr<Subaction::Private> getSubaction(const std::string &path);
|
||||
|
||||
/// Add an action set
|
||||
void addActionSet(ActionSet::Private *actionSet)
|
||||
{
|
||||
_actionSets.insert(actionSet);
|
||||
_actionsUpdated = true;
|
||||
}
|
||||
|
||||
/// Remove an action set
|
||||
void removeActionSet(ActionSet::Private *actionSet)
|
||||
{
|
||||
_actionSets.erase(actionSet);
|
||||
_actionsUpdated = true;
|
||||
}
|
||||
|
||||
/// Add an interaction profile
|
||||
void addInteractionProfile(InteractionProfile::Private *interactionProfile)
|
||||
{
|
||||
_interactionProfiles.insert(interactionProfile);
|
||||
_actionsUpdated = true;
|
||||
}
|
||||
|
||||
/// Remove an interaction profile
|
||||
void removeInteractionProfile(InteractionProfile::Private *interactionProfile)
|
||||
{
|
||||
_interactionProfiles.erase(interactionProfile);
|
||||
_actionsUpdated = true;
|
||||
}
|
||||
|
||||
/// Get the current interaction profile for the given subaction path.
|
||||
InteractionProfile *getCurrentInteractionProfile(const OpenXR::Path &subactionPath) const;
|
||||
|
||||
/// Get a string describing the state (for user consumption).
|
||||
const char *getStateString() const;
|
||||
|
||||
// Initialize information required for setting up VR
|
||||
void init(osgViewer::GraphicsWindow *window,
|
||||
osgViewer::View *view = nullptr)
|
||||
{
|
||||
_window = window;
|
||||
_view = view;
|
||||
}
|
||||
|
||||
/// Update down state depending on any changed settings.
|
||||
void syncSettings();
|
||||
|
||||
/// Find whether actions have been updated.
|
||||
bool getActionsUpdated() const;
|
||||
|
||||
/// Arrange reinit as needed of action setup.
|
||||
void syncActionSetup();
|
||||
|
||||
/// Add a composition layer
|
||||
void addCompositionLayer(CompositionLayer::Private *layer);
|
||||
|
||||
/// Remove a composition layer
|
||||
void removeCompositionLayer(CompositionLayer::Private *layer);
|
||||
|
||||
/// Find whether state has changed since last call, and reset.
|
||||
bool checkAndResetStateChanged();
|
||||
|
||||
/// Perform a regular update.
|
||||
void update();
|
||||
|
||||
// Extending OpenXR::EventManager
|
||||
void onInstanceLossPending(OpenXR::Instance *instance,
|
||||
const XrEventDataInstanceLossPending *event) override;
|
||||
void onInteractionProfileChanged(OpenXR::Session *session,
|
||||
const XrEventDataInteractionProfileChanged *event) override;
|
||||
void onSessionStateChanged(OpenXR::Session *session,
|
||||
const XrEventDataSessionStateChanged *event) override;
|
||||
void onSessionStateStart(OpenXR::Session *session) override;
|
||||
void onSessionStateEnd(OpenXR::Session *session, bool retry) override;
|
||||
void onSessionStateReady(OpenXR::Session *session) override;
|
||||
void onSessionStateStopping(OpenXR::Session *session, bool loss) override;
|
||||
void onSessionStateFocus(OpenXR::Session *session) override;
|
||||
void onSessionStateUnfocus(OpenXR::Session *session) override;
|
||||
|
||||
osg::ref_ptr<OpenXR::Session::Frame> getFrame(osg::FrameStamp *stamp);
|
||||
void startRendering(osg::FrameStamp *stamp);
|
||||
void endFrame(osg::FrameStamp *stamp);
|
||||
|
||||
void updateSlave(uint32_t viewIndex, osg::View& view,
|
||||
osg::View::Slave& slave);
|
||||
void updateVisibilityMaskTransform(osg::Camera *camera,
|
||||
osg::MatrixTransform *transform);
|
||||
|
||||
osg::Matrixd getEyeProjection(osg::FrameStamp *stamp,
|
||||
uint32_t viewIndex,
|
||||
const osg::Matrixd& projection);
|
||||
osg::Matrixd getEyeView(osg::FrameStamp *stamp, uint32_t viewIndex,
|
||||
const osg::Matrixd& view);
|
||||
|
||||
void initialDrawCallback(osg::RenderInfo &renderInfo);
|
||||
void releaseGLObjects(osg::State *state);
|
||||
void swapBuffersImplementation(osg::GraphicsContext* gc);
|
||||
|
||||
inline osg::ref_ptr<OpenXR::CompositionLayerProjection> getProjectionLayer()
|
||||
{
|
||||
return _projectionLayer;
|
||||
}
|
||||
|
||||
class TextureRect
|
||||
{
|
||||
public:
|
||||
|
||||
float x, y;
|
||||
float width, height;
|
||||
|
||||
TextureRect(const OpenXR::SwapchainGroup::SubImage &subImage)
|
||||
{
|
||||
float w = subImage.getSwapchainGroup()->getWidth();
|
||||
float h = subImage.getSwapchainGroup()->getHeight();
|
||||
x = (float)subImage.getX() / w;
|
||||
y = (float)subImage.getY() / h;
|
||||
width = (float)subImage.getWidth() / w;
|
||||
height = (float)subImage.getHeight() / h;
|
||||
}
|
||||
};
|
||||
|
||||
unsigned int getViewCount() const
|
||||
{
|
||||
return _xrViews.size();
|
||||
}
|
||||
|
||||
TextureRect getViewTextureRect(unsigned int viewIndex) const
|
||||
{
|
||||
return TextureRect(_xrViews[viewIndex]->getSubImage());
|
||||
}
|
||||
|
||||
// Caller must validate viewIndex using getViewCount()
|
||||
osg::ref_ptr<osg::Texture2D> getViewTexture(unsigned int viewIndex,
|
||||
const osg::FrameStamp *stamp) const
|
||||
{
|
||||
return _xrViews[viewIndex]->getSwapchain()->getOsgTexture(stamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an RGBA swapchain format.
|
||||
* @param bestRGBBits Desired number of combined RGB bits.
|
||||
* @param bestAlphaBits Desired number of alpha bits.
|
||||
* @param preferredRGBEncodingMask Mask of preferred RGB encodings (see
|
||||
* Settings::Encoding).
|
||||
* @param allowedRGBEncodingMask Mask of allowed RGB encodings (see
|
||||
* Settings::Encoding).
|
||||
* @return The chosen OpenGL swapchain format.
|
||||
*/
|
||||
int64_t chooseRGBAFormat(unsigned int bestRGBBits,
|
||||
unsigned int bestAlphaBits,
|
||||
uint32_t preferredRGBEncodingMask,
|
||||
uint32_t allowedRGBEncodingMask) const;
|
||||
/**
|
||||
* Choose a fallback depth / stencil format for use if the OpenXR
|
||||
* runtime doesn't support depth swapchains.
|
||||
* @param bestDepthBits Desired number of depth bits.
|
||||
* @param bestStencilBits Desired number of stencil bits.
|
||||
* @param preferredDepthEncodingMask Mask of preferred depth encodings
|
||||
* (see Settings::Encoding).
|
||||
* @param allowedDepthEncodingMask Mask of allowed depth encodings
|
||||
* (see Settings::Encoding).
|
||||
* @return The chosen fallback OpenGL depth / stencil format.
|
||||
*/
|
||||
GLenum chooseFallbackDepthFormat(unsigned int bestDepthBits,
|
||||
unsigned int bestStencilBits,
|
||||
uint32_t preferredDepthEncodingMask,
|
||||
uint32_t allowedDepthEncodingMask) const;
|
||||
/**
|
||||
* Choose a depth / stencil swapchain format for submission to OpenXR.
|
||||
* @param bestDepthBits Desired number of depth bits.
|
||||
* @param bestStencilBits Desired number of stencil bits.
|
||||
* @param preferredDepthEncodingMask Mask of preferred depth encodings
|
||||
* (see Settings::Encoding).
|
||||
* @param allowedDepthEncodingMask Mask of allowed depth encodings
|
||||
* (see Settings::Encoding).
|
||||
* @return The chosen OpenGL depth / stencil swapchain format.
|
||||
*/
|
||||
int64_t chooseDepthFormat(unsigned int bestDepthBits,
|
||||
unsigned int bestStencilBits,
|
||||
uint32_t preferredDepthEncodingMask,
|
||||
uint32_t allowedDepthEncodingMask) const;
|
||||
|
||||
protected:
|
||||
|
||||
typedef enum {
|
||||
/// Successfully completed operation.
|
||||
UP_SUCCESS,
|
||||
/// Operation not possible at the moment, try again soon.
|
||||
UP_SOON,
|
||||
/// Operation not possible at the moment, try again later.
|
||||
UP_LATER,
|
||||
/// Operation permanently failed, disable VR.
|
||||
UP_ABORT,
|
||||
} UpResult;
|
||||
|
||||
typedef enum {
|
||||
/// Successfully completed operation.
|
||||
DOWN_SUCCESS,
|
||||
/// Operation not possible at the moment, try again soon.
|
||||
DOWN_SOON,
|
||||
} DownResult;
|
||||
|
||||
// Pre-instance probing
|
||||
void probe() const;
|
||||
void unprobe() const;
|
||||
|
||||
// These are called during update to raise or lower VR state level
|
||||
UpResult upInstance();
|
||||
DownResult downInstance();
|
||||
UpResult upSystem();
|
||||
DownResult downSystem();
|
||||
UpResult upSession();
|
||||
DownResult downSession();
|
||||
UpResult upActions();
|
||||
DownResult downActions();
|
||||
|
||||
// Set up a single swapchain containing multiple viewports
|
||||
bool setupSingleSwapchain(int64_t format, int64_t depthFormat = 0,
|
||||
GLenum fallbackDepthFormat = 0);
|
||||
// Set up a swapchain for each view
|
||||
bool setupMultipleSwapchains(int64_t format, int64_t depthFormat = 0,
|
||||
GLenum fallbackDepthFormat = 0);
|
||||
// Set up slave cameras
|
||||
void setupSlaveCameras();
|
||||
// Set up SceneView VR mode cameras
|
||||
void setupSceneViewCameras();
|
||||
void setupSceneViewCamera(osg::Camera *camera);
|
||||
// Visibility mask setup
|
||||
inline bool needsVisibilityMask(osg::Camera *camera)
|
||||
{
|
||||
return _useVisibilityMask &&
|
||||
(camera->getClearMask() & GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
void setupSceneViewVisibilityMasks(osg::Camera *camera,
|
||||
osg::ref_ptr<osg::MatrixTransform> &transform);
|
||||
osg::ref_ptr<osg::Geode> setupVisibilityMask(osg::Camera *camera,
|
||||
uint32_t viewIndex,
|
||||
osg::ref_ptr<osg::MatrixTransform> &transform);
|
||||
|
||||
osg::ref_ptr<Settings> _settings;
|
||||
Settings _settingsCopy;
|
||||
osg::observer_ptr<Manager> _manager;
|
||||
|
||||
// app configuration
|
||||
osg::Node::NodeMask _visibilityMaskLeft;
|
||||
osg::Node::NodeMask _visibilityMaskRight;
|
||||
|
||||
// Actions
|
||||
bool _actionsUpdated;
|
||||
std::set<ActionSet::Private *> _actionSets;
|
||||
std::set<InteractionProfile::Private *> _interactionProfiles;
|
||||
std::map<std::string, std::weak_ptr<Subaction::Private>> _subactions;
|
||||
|
||||
// Composition layers
|
||||
bool _compositionLayersUpdated;
|
||||
std::list<CompositionLayer::Private *> _compositionLayers;
|
||||
|
||||
/// Current state of OpenXR initialization.
|
||||
VRState _currentState;
|
||||
/// State of OpenXR initialisation to drop down to.
|
||||
VRState _downState;
|
||||
/// State of OpenXR initialisation to rise up to.
|
||||
VRState _upState;
|
||||
/// Number of attempts made to rise VR state.
|
||||
unsigned int _upDelay;
|
||||
/// Whether probing should be kept active.
|
||||
bool _probing;
|
||||
/// Last read state as a user readable string.
|
||||
mutable std::string _stateString;
|
||||
/// Whether state has changed since the last update.
|
||||
bool _stateChanged;
|
||||
|
||||
// Session setup
|
||||
osg::observer_ptr<osgViewer::ViewerBase> _viewer;
|
||||
osg::observer_ptr<osgViewer::GraphicsWindow> _window;
|
||||
osg::observer_ptr<osgViewer::View> _view;
|
||||
|
||||
// Pre-Instance related
|
||||
mutable bool _probed;
|
||||
mutable bool _hasValidationLayer;
|
||||
mutable bool _hasDepthInfoExtension;
|
||||
mutable bool _hasVisibilityMaskExtension;
|
||||
|
||||
// Instance related
|
||||
osg::ref_ptr<OpenXR::Instance> _instance;
|
||||
bool _useDepthInfo;
|
||||
bool _useVisibilityMask;
|
||||
|
||||
// System related
|
||||
XrFormFactor _formFactor;
|
||||
OpenXR::System *_system;
|
||||
const OpenXR::System::ViewConfiguration *_chosenViewConfig;
|
||||
XrEnvironmentBlendMode _chosenEnvBlendMode;
|
||||
|
||||
// Session related
|
||||
VRMode _vrMode;
|
||||
SwapchainMode _swapchainMode;
|
||||
osg::ref_ptr<OpenXR::Session> _session;
|
||||
std::vector<osg::ref_ptr<XRView> > _xrViews;
|
||||
std::vector<osg::ref_ptr<AppView> > _appViews;
|
||||
FrameStore _frames;
|
||||
osg::ref_ptr<OpenXR::CompositionLayerProjection> _projectionLayer;
|
||||
OpenXR::DepthInfo _depthInfo;
|
||||
osg::ref_ptr<osg::DisplaySettings> _stereoDisplaySettings;
|
||||
};
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_XRSTATE_CALLBACKS
|
||||
#define OSGXR_XRSTATE_CALLBACKS 1
|
||||
|
||||
#include "XRState.h"
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/GraphicsContext>
|
||||
#include <osg/View>
|
||||
|
||||
#include <osgUtil/SceneView>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
class SlaveCamsUpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
||||
{
|
||||
public:
|
||||
|
||||
SlaveCamsUpdateSlaveCallback(uint32_t viewIndex,
|
||||
XRState *xrState,
|
||||
osg::MatrixTransform *visMaskTransform) :
|
||||
_viewIndex(viewIndex),
|
||||
_xrState(xrState),
|
||||
_visMaskTransform(visMaskTransform)
|
||||
{
|
||||
}
|
||||
|
||||
void updateSlave(osg::View& view, osg::View::Slave& slave) override
|
||||
{
|
||||
_xrState->updateSlave(_viewIndex, view, slave);
|
||||
if (_visMaskTransform.valid())
|
||||
_xrState->updateVisibilityMaskTransform(slave._camera,
|
||||
_visMaskTransform.get());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t _viewIndex;
|
||||
osg::observer_ptr<XRState> _xrState;
|
||||
osg::observer_ptr<osg::MatrixTransform> _visMaskTransform;
|
||||
};
|
||||
|
||||
class SceneViewUpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
||||
{
|
||||
public:
|
||||
|
||||
SceneViewUpdateSlaveCallback(osg::ref_ptr<XRState> xrState,
|
||||
osg::ref_ptr<osg::MatrixTransform> visMaskTransform) :
|
||||
_xrState(xrState),
|
||||
_visMaskTransform(visMaskTransform)
|
||||
{
|
||||
}
|
||||
|
||||
void updateSlave(osg::View& view, osg::View::Slave& slave) override
|
||||
{
|
||||
if (_visMaskTransform.valid())
|
||||
_xrState->updateVisibilityMaskTransform(slave._camera,
|
||||
_visMaskTransform.get());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState> _xrState;
|
||||
osg::observer_ptr<osg::MatrixTransform> _visMaskTransform;
|
||||
};
|
||||
|
||||
class ComputeStereoMatricesCallback : public osgUtil::SceneView::ComputeStereoMatricesCallback
|
||||
{
|
||||
public:
|
||||
|
||||
ComputeStereoMatricesCallback(XRState *xrState,
|
||||
osgUtil::SceneView *sceneView) :
|
||||
_xrState(xrState),
|
||||
_sceneView(sceneView)
|
||||
{
|
||||
}
|
||||
|
||||
osg::Matrixd computeLeftEyeProjection(const osg::Matrixd& projection) const override
|
||||
{
|
||||
return _xrState->getEyeProjection(_sceneView->getFrameStamp(),
|
||||
0, projection);
|
||||
}
|
||||
|
||||
osg::Matrixd computeLeftEyeView(const osg::Matrixd& view) const override
|
||||
{
|
||||
return _xrState->getEyeView(_sceneView->getFrameStamp(),
|
||||
0, view);
|
||||
}
|
||||
|
||||
osg::Matrixd computeRightEyeProjection(const osg::Matrixd& projection) const override
|
||||
{
|
||||
return _xrState->getEyeProjection(_sceneView->getFrameStamp(),
|
||||
1, projection);
|
||||
}
|
||||
|
||||
osg::Matrixd computeRightEyeView(const osg::Matrixd& view) const override
|
||||
{
|
||||
return _xrState->getEyeView(_sceneView->getFrameStamp(),
|
||||
1, view);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState> _xrState;
|
||||
osg::observer_ptr<osgUtil::SceneView> _sceneView;
|
||||
};
|
||||
|
||||
class InitialDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
|
||||
InitialDrawCallback(osg::ref_ptr<XRState> xrState) :
|
||||
_xrState(xrState)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
_xrState->initialDrawCallback(renderInfo);
|
||||
}
|
||||
|
||||
void releaseGLObjects(osg::State* state) const override
|
||||
{
|
||||
_xrState->releaseGLObjects(state);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState> _xrState;
|
||||
};
|
||||
|
||||
class PreDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
|
||||
PreDrawCallback(osg::ref_ptr<XRState::XRSwapchain> xrSwapchain) :
|
||||
_xrSwapchain(xrSwapchain)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
_xrSwapchain->preDrawCallback(renderInfo);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState::XRSwapchain> _xrSwapchain;
|
||||
};
|
||||
|
||||
class PostDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
|
||||
PostDrawCallback(osg::ref_ptr<XRState::XRSwapchain> xrSwapchain) :
|
||||
_xrSwapchain(xrSwapchain)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
_xrSwapchain->postDrawCallback(renderInfo);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<XRState::XRSwapchain> _xrSwapchain;
|
||||
};
|
||||
|
||||
class SwapCallback : public osg::GraphicsContext::SwapCallback
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SwapCallback(osg::ref_ptr<XRState> xrState) :
|
||||
_xrState(xrState),
|
||||
_frameIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
void swapBuffersImplementation(osg::GraphicsContext* gc)
|
||||
{
|
||||
_xrState->swapBuffersImplementation(gc);
|
||||
}
|
||||
|
||||
int frameIndex() const
|
||||
{
|
||||
return _frameIndex;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
osg::observer_ptr<XRState> _xrState;
|
||||
int _frameIndex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#include <osgXR/MirrorSettings>
|
||||
#include <osgXR/OpenXRDisplay>
|
||||
#include <osgXR/Settings>
|
||||
#include <osgXR/osgXR>
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/os_utils>
|
||||
|
||||
using namespace osgXR;
|
||||
|
||||
void osgXR::setupViewerDefaults(osgViewer::Viewer *viewer,
|
||||
const std::string &appName,
|
||||
uint32_t appVersion)
|
||||
{
|
||||
unsigned int vr = 0;
|
||||
osg::getEnvVar("OSGXR", vr);
|
||||
|
||||
if (vr)
|
||||
{
|
||||
Settings *settings = Settings::instance();
|
||||
MirrorSettings *mirrorSettings = &settings->getMirrorSettings();
|
||||
std::string value;
|
||||
|
||||
Settings::VRMode vrMode = Settings::VRMODE_AUTOMATIC;
|
||||
if (osg::getEnvVar("OSGXR_MODE", value))
|
||||
{
|
||||
if (value == "SLAVE_CAMERAS")
|
||||
vrMode = Settings::VRMODE_SLAVE_CAMERAS;
|
||||
else if (value == "SCENE_VIEW")
|
||||
vrMode = Settings::VRMODE_SCENE_VIEW;
|
||||
}
|
||||
|
||||
Settings::SwapchainMode swapchainMode = Settings::SWAPCHAIN_AUTOMATIC;
|
||||
if (osg::getEnvVar("OSGXR_SWAPCHAIN", value))
|
||||
{
|
||||
if (value == "MULTIPLE")
|
||||
swapchainMode = Settings::SWAPCHAIN_MULTIPLE;
|
||||
else if (value == "SINGLE")
|
||||
swapchainMode = Settings::SWAPCHAIN_SINGLE;
|
||||
}
|
||||
|
||||
float unitsPerMeter = 0.0f;
|
||||
osg::getEnvVar("OSGXR_UNITS_PER_METER", unitsPerMeter);
|
||||
|
||||
int validationLayer = 0;
|
||||
osg::getEnvVar("OSGXR_VALIDATION_LAYER", validationLayer);
|
||||
|
||||
int depthInfo = 0;
|
||||
osg::getEnvVar("OSGXR_DEPTH_INFO", depthInfo);
|
||||
|
||||
MirrorSettings::MirrorMode mirrorMode = MirrorSettings::MIRROR_AUTOMATIC;
|
||||
int mirrorViewIndex = -1;
|
||||
if (osg::getEnvVar("OSGXR_MIRROR", value))
|
||||
{
|
||||
if (value == "NONE")
|
||||
{
|
||||
mirrorMode = MirrorSettings::MIRROR_NONE;
|
||||
}
|
||||
else if (value == "LEFT")
|
||||
{
|
||||
mirrorMode = MirrorSettings::MIRROR_SINGLE;
|
||||
mirrorViewIndex = 0;
|
||||
}
|
||||
else if (value == "RIGHT")
|
||||
{
|
||||
mirrorMode = MirrorSettings::MIRROR_SINGLE;
|
||||
mirrorViewIndex = 1;
|
||||
}
|
||||
else if (value == "LEFT_RIGHT")
|
||||
{
|
||||
mirrorMode = MirrorSettings::MIRROR_LEFT_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
settings->setApp(appName, appVersion);
|
||||
settings->setFormFactor(Settings::HEAD_MOUNTED_DISPLAY);
|
||||
settings->preferEnvBlendMode(Settings::BLEND_MODE_OPAQUE);
|
||||
if (unitsPerMeter > 0.0f)
|
||||
settings->setUnitsPerMeter(unitsPerMeter);
|
||||
settings->setVRMode(vrMode);
|
||||
settings->setSwapchainMode(swapchainMode);
|
||||
settings->setValidationLayer(!!validationLayer);
|
||||
settings->setDepthInfo(!!depthInfo);
|
||||
mirrorSettings->setMirror(mirrorMode, mirrorViewIndex);
|
||||
|
||||
osg::ref_ptr<OpenXRDisplay> xr = new OpenXRDisplay(settings);
|
||||
viewer->apply(xr);
|
||||
|
||||
OSG_WARN << "Setting up VR" << std::endl;
|
||||
}
|
||||
}
|
||||
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
// =============================================================================
|
||||
// Derived from openxr-simple-example
|
||||
// Copyright 2019-2021, Collabora, Ltd.
|
||||
// Which was adapted from
|
||||
// https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/common/xr_linear.h
|
||||
// Copyright (c) 2017 The Khronos Group Inc.
|
||||
// Copyright (c) 2016 Oculus VR, LLC.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// =============================================================================
|
||||
|
||||
#include "projection.h"
|
||||
|
||||
void osgXR::createProjectionFov(osg::Matrix& result,
|
||||
const XrFovf& fov,
|
||||
const float nearZ,
|
||||
const float farZ)
|
||||
{
|
||||
const float tanAngleLeft = tanf(fov.angleLeft);
|
||||
const float tanAngleRight = tanf(fov.angleRight);
|
||||
|
||||
const float tanAngleDown = tanf(fov.angleDown);
|
||||
const float tanAngleUp = tanf(fov.angleUp);
|
||||
|
||||
const float tanAngleWidth = tanAngleRight - tanAngleLeft;
|
||||
|
||||
// Set to tanAngleDown - tanAngleUp for a clip space with positive Y
|
||||
// down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with
|
||||
// positive Y up (OpenGL / D3D / Metal).
|
||||
const float tanAngleHeight = tanAngleUp - tanAngleDown;
|
||||
|
||||
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
|
||||
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
|
||||
const float offsetZ = nearZ;
|
||||
|
||||
if (farZ <= nearZ)
|
||||
{
|
||||
// place the far plane at infinity
|
||||
result(0, 0) = 2 / tanAngleWidth;
|
||||
result(1, 0) = 0;
|
||||
result(2, 0) = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
||||
result(3, 0) = 0;
|
||||
|
||||
result(0, 1) = 0;
|
||||
result(1, 1) = 2 / tanAngleHeight;
|
||||
result(2, 1) = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
||||
result(3, 1) = 0;
|
||||
|
||||
result(0, 2) = 0;
|
||||
result(1, 2) = 0;
|
||||
result(2, 2) = -1;
|
||||
result(3, 2) = -(nearZ + offsetZ);
|
||||
|
||||
result(0, 3) = 0;
|
||||
result(1, 3) = 0;
|
||||
result(2, 3) = -1;
|
||||
result(3, 3) = 0;
|
||||
} else {
|
||||
// normal projection
|
||||
result(0, 0) = 2 / tanAngleWidth;
|
||||
result(1, 0) = 0;
|
||||
result(2, 0) = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
||||
result(3, 0) = 0;
|
||||
|
||||
result(0, 1) = 0;
|
||||
result(1, 1) = 2 / tanAngleHeight;
|
||||
result(2, 1) = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
||||
result(3, 1) = 0;
|
||||
|
||||
result(0, 2) = 0;
|
||||
result(1, 2) = 0;
|
||||
result(2, 2) = -(farZ + offsetZ) / (farZ - nearZ);
|
||||
result(3, 2) = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
|
||||
|
||||
result(0, 3) = 0;
|
||||
result(1, 3) = 0;
|
||||
result(2, 3) = -1;
|
||||
result(3, 3) = 0;
|
||||
}
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2021 James Hogan <james@albanarts.com>
|
||||
|
||||
#ifndef OSGXR_PROJECTION
|
||||
#define OSGXR_PROJECTION 1
|
||||
|
||||
#include <osg/Matrix>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace osgXR {
|
||||
|
||||
void createProjectionFov(osg::Matrix& result,
|
||||
const XrFovf& fov,
|
||||
const float nearZ,
|
||||
const float farZ);
|
||||
|
||||
} // osgXR
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user