1
0
Fork 0

3rdparty: Import osgXR 0.3.7+

Import osgXR from https://github.com/amalon/osgXR master branch into
3rdparty, specifically commit b7e222775553b529018ac4b847353327c24ae5d4,
which is 0.3.7 with tweaks for building as a subproject in a
subdirectory.

This will allow VR support to be more conveniently built if not already
installed, without having to fetch yet another dependency.
This commit is contained in:
James Hogan 2022-01-13 16:23:51 +00:00
parent 31956f1f33
commit 0431e7cb3c
No known key found for this signature in database
GPG key ID: 35CEE4862B1023F2
83 changed files with 12256 additions and 0 deletions

184
3rdparty/osgXR/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,184 @@
Version 0.3.7
-------------
New features:
* Implement basic Windows OpenGL graphics binding.
Windows (MSVC) build fixes:
* Session: #ifdef X11 specific workaround.
* CMake: Require osgUtil.
* Manager: Avoid undefined Mirror.
* Fix Win32 DLL imports/exports.
* Subaction: Use C++11 smart pointers for \_private to avoid undefined Private
implementation class.
* Fix build against old OpenGL headers.
ABI changes (ABI version 5):
* Subaction: Use C++11 smart pointers for \_private.
* Switch Action, ActionSet & InteractionProfile pimpls to std::unique\_ptr<>.
Version 0.3.6
-------------
Bug fixes:
* Work around Monado GL context assumptions for xrCreateSession &
xrCreateSwapchain calls.
* Permit new swapchain formats: GL\_RGB10\_A2 (for Monado on AMD) & GL\_RGBA8.
Behaviour changes:
* Report list of swapchain formats on failure to choose one.
Behind the scenes:
* Minor whitespace cleanups in src/XRState.cpp.
Version 0.3.5
-------------
Bug fixes:
* Ensure OpenXR::Action is valid before creating an action state object.
* Don't suggest empty InteractionProfile bindings to OpenXR.
Behaviour changes:
* Manager (XRState) no longer keeps counted references to ActionSets or
InteractionProfiles. The app should manage the lifetime of these objects
itself, and can now safely discard and recreate them.
* All created Actions are now provided to OpenXR even if unreferenced by any
InteractionProfile suggested bindings.
* Action setup is now treated as a separate initialisation stage. As such if
no action sets or no interaction profiles have been created, action setup
will now take place on the next update() after they are created.
New/expanded APIs:
* Manager::syncActionSet() - To inform osgXR that actions, action sets, or
interaction profiles have been altered, so it can take action to apply them
as soon as possible.
* ActionPose::Location::operator !=, ActionPose::Location::operator == - For
comparing pose location objects for equality.
Behind the scenes:
* Some minor refactoring in src/Action.cpp.
Version 0.3.4
-------------
Bug fixes:
* Fix draw pass accounting and slave cam VR mode.
Behaviour changes:
* Automatically fall back from SceneView mode if the view configuration isn't
stereo.
New/expanded APIs:
* New action APIs for exposing OpenXR actions (both input and haptics), action
sets, interaction profiles, and subactions:
* osgXR/Action: New Action, ActionBoolean, ActionFloat, ActionVector2f,
ActionPose and ActionVibration classes to represent different kinds of
XrAction.
* osgXR/ActionSet: New ActionSet class to group actions that can be activated
and deactivated together.
* osgXR/InteractionProfile: New InteractionProfile class to allows default
action bindings for interaction profiles to be suggested.
* osgXR/Subaction: New Subaction class to represent a subaction path (or top
level user path) which groups physical interactions, allowing single
actions that apply to both hands to be handled separately.
Behind the scenes:
* Add internal action management classes in OpenXR namespace.
* Wrap XrSpace in an OpenXR::Space class.
* Wrap XrPath in an OpenXR::Path class.
* Extend inline code documentation.
* Code cleanups.
Version 0.3.3 (formerly 0.4.0)
------------------------------
New/expanded APIs:
* Settings::getVisibilityMask(), Settings::getVisibilityMask() - for setting
whether osgXR should create visibility masks (when supported by the OpenXR
runtime).
* Manager::hasVisibilityMaskExtension() - for finding whether the visibility
mask extension is supported by the OpenXR runtime.
* Manager::setVisibilityMaskNodeMasks() - for setting the left and right eye
NodeMasks to use for visibility masks.
Behind the scenes:
* Implement creation, caching, and updating of visibility mask geometry for each
OpenXR view.
* Implement rendering of visibility masks to the depth buffer to reduce fragment
overhead when GPU bound.
Version 0.3.2
-------------
Bug fixes:
* Fix a couple of bugs around session recreation (used when VR or swapchain mode
changes).
Behaviour changes:
* Pick depth buffer format based on GraphicsContext traits depth bits.
* Enable depth info submission at session state to allow it to be dynamically
switched without hitting a SteamVR hang during instance destruction.
Behind the scenes:
* Fix a few inconsequential compiler warnings with -Wall.
* Minor cosmetic cleanups (whitespace, explicit include).
Version 0.3.1
-------------
Behind the scenes:
* Fix possible crash in XRState::isRunning() if session is delayed coming up.
Version 0.3.0
-------------
API changes:
* Manager::checkAndResetStateChanged() - to detect when VR state may have
changed, requiring app caches to be invalidated or synchronised with the new
state.
Version 0.2.1
-------------
Behind the scenes:
* Make frame view location accessors thread safe so multiple cull threads can be
used.
Version 0.2.0
-------------
API changes:
* Cleanups (moving dynamic bits out of Settings).
* Manager::update() - should be called regularly to allow for incremental
bringup and OpenXR event handling.
* Manager::setEnabled() - for triggering bringing up/down of VR.
New/expanded APIs:
* Manager::destroyAndWait() and Manager::isDestroying() - for clean shutdown
before program exit.
* Manager::syncSettings() - to trigger appropriate reinitialisation to handle
any changed settings.
* Manager::getStateString() - to get a user readable description of the current
VR state.
* Manager::onRunning() - virtual callback when VR has started (consider setting
up desktop mirrors).
* Manager::onStopped() - virtual callback when VR has stopped (consider
removing desktop mirrors).
* Manager::onFocus() - virtual callback when VR app is running in focus
(consider resuming if paused).
* Manager::onUnfocus() - virtual callback when VR app has lost focus (consider
pausing the experience).
Behind the scenes highlights:
* Make OpenXR bringup incremental, reversible and restartable.
* Improved handling of SteamVR's messing with GL context and threading.
* Separated event handling.
Version 0.1.0
-------------
This represents early development with the API still in heavy flux.
It supported:
* A Manager class with virtual callbacks for configuring views.
* Desktop mirrors of the VR views.

79
3rdparty/osgXR/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,79 @@
# Top level CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
set(osgXR_MAJOR_VERSION 0)
set(osgXR_MINOR_VERSION 3)
set(osgXR_PATCH_VERSION 7)
set(osgXR_SOVERSION 5)
set(osgXR_VERSION "${osgXR_MAJOR_VERSION}.${osgXR_MINOR_VERSION}.${osgXR_PATCH_VERSION}")
project(osgXR
VERSION ${osgXR_VERSION}
DESCRIPTION "OpenXR integration for OpenSceneGraph applications"
)
if(CMAKE_PROJECT_NAME STREQUAL osgXR)
# Normal top level project build, install package components
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
# Build options
option(BUILD_SHARED_LIBS "Whether to build as a shared library" ON)
option(BUILD_OSGXR_EXAMPLES "Enable to build osgXR examples" OFF)
# Source files in src/
add_subdirectory(src)
if(BUILD_OSGXR_EXAMPLES)
add_subdirectory(examples)
endif()
set(INSTALL_INCDIR "${CMAKE_INSTALL_INCLUDEDIR}")
# Preprocess pkgconfig file
configure_file(osgXR.pc.in osgXR.pc @ONLY)
# Preprocess package config
configure_package_config_file(Config.cmake.in osgXRConfig.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/osgXR"
PATH_VARS INSTALL_INCDIR
)
# Build package version file
write_basic_package_version_file(osgXRConfigVersion.cmake
VERSION "${PROJECT_VERSION}"
COMPATIBILITY SameMinorVersion
)
# Install library and headers
install(TARGETS osgXR
EXPORT osgXRTargets
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
PUBLIC_HEADER DESTINATION "${INSTALL_INCDIR}/osgXR"
INCLUDES DESTINATION "${INSTALL_INCDIR}"
)
# Install pkgconfig file
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/osgXR.pc"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig"
)
# Install export targets
install(EXPORT osgXRTargets
FILE osgXRTargets.cmake
NAMESPACE osgXR::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/osgXR"
)
# Install package config and version files
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/osgXRConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/osgXRConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/osgXR"
)
else()
# Subproject build
# Just build src/ as a static library
set(osgXR_LIBRARY_TYPE STATIC)
add_subdirectory(src)
endif()

14
3rdparty/osgXR/Config.cmake.in vendored Normal file
View file

@ -0,0 +1,14 @@
include(CMakeFindDependencyMacro)
find_dependency(OpenGL)
find_dependency(OpenSceneGraph COMPONENTS osgViewer)
find_dependency(OpenXR)
@PACKAGE_INIT@
set_and_check(osgXR_INCLUDE_DIR @PACKAGE_INSTALL_INCDIR@)
set(osgXR_LIBRARY osgXR::osgXR)
include("${CMAKE_CURRENT_LIST_DIR}/osgXRTargets.cmake")
check_required_components(osgXR)

502
3rdparty/osgXR/LICENSE.txt vendored Normal file
View file

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

73
3rdparty/osgXR/README.md vendored Normal file
View file

@ -0,0 +1,73 @@
osgXR: Virtual Reality with OpenXR and OpenSceneGraph
=====================================================
This library is to allow Virtual Reality support to be added to
[OpenSceneGraph](http://www.openscenegraph.org/) applications, using the
[OpenXR](https://www.khronos.org/OpenXR/) standard.
Status:
* Still in development, contributions welcome. Plenty of work to do.
* APIs to support OpenXR VR display output in OpenSceneGraph apps.
* APIs to support OpenXR's action based input and haptic output for controller
interaction.
* OpenGL graphics bindings for Linux (X11/Xlib) and Windows (note that WMR
only supports DirectX bindings).
License: LGPL 2.1
Dependencies: OpenSceneGraph, OpenXR
Links:
* Matrix room: [#osgxr:hoganfam.uk](https://matrix.to/#/#osgxr:hoganfam.uk?via=hoganfam.uk)
* [OpenXR specifications](https://www.khronos.org/registry/OpenXR/#apispecs)
Installation
------------
Something like this:
```shell
mkdir build
cd build
cmake ..
make
make install
```
Getting Started
---------------
To import osgXR into a CMake based project, you can use the included CMake
module, adding something like this to your CMakeLists.txt:
```cmake
find_package(osgXR 0.3.5 REQUIRED)
target_link_libraries(target
..
osgXR::osgXR
)
```
osgXR can also optionally be built as a subproject. Consider using ``git
subtree`` to import osgXR, then use something like this:
```cmake
add_subdirectory(osgXR)
target_link_libraries(target
..
osgXR
)
```
If you have installed osgXR outside of the system prefix (CMake's default prefix
on UNIX systems is ``/usr/local``), you may need to tell CMake where to find it
when you configure the project. You can do this by defining ``osgXR_DIR`` when
invoking cmake, e.g. with the argument ``-DosgXR_DIR=$PREFIX/lib/cmake/osgXR``
where ``$PREFIX`` is osgXR's install prefix (``CMAKE_INSTALL_PREFIX``).
The Public API
--------------
See the [API documentation](docs/API.md) for details of the API.

102
3rdparty/osgXR/docs/API.md vendored Normal file
View file

@ -0,0 +1,102 @@
osgXR API Documentation
=======================
The osgXR API is currently considered unstable, however it is versioned. Only
matching minor version numbers should be expected to be source and binary
compatible with one another.
The osgXR headers can be found in [include/osgXR/](../include/osgXR/).
The initial version of this library exposed ``<osgXR/OpenXRDisplay>`` for
configuring a view for VR, and ``<osgXR/osgXR>`` with a convenience wrapper
``osgXR::setupViewerDefaults`` to set up VR automatically based on environment
variables. This worked for most simple OpenSceneGraph examples, however for real
projects something more capable is needed, so it is likely these will be removed
in a future version (they are not currently working due to the new
``XRState::update()`` based state machine).
It is instead recommended to extend the ``osgXR::Manager`` class from
``<osgXR/Manager>`` and implement the callbacks.
## <[osgXR/Action](../include/osgXR/Action)>
This header provides the ``osgXR::Action`` base class, and the
``osgXR::ActionBoolean``, ``osgXR::ActionFloat``, ``osgXR::ActionVector2f``,
``osgXR::ActionPose`` and ``osgXR::ActionVibration`` classes which an
application can use to define OpenXR actions, read input state, and send haptic
output.
## <[osgXR/ActionSet](../include/osgXR/ActionSet)>
This header provides the ``osgXR::ActionSet`` class which an application uses
to group actions into groups which can be separately activated and deactivated.
## <[osgXR/InteractionProfile](../include/osgXR/InteractionProfile)>
This header provides the ``osgXR::InteractionProfile`` class which an
application uses to suggest bindings between OpenXR actions and the physical
interactions of a given OpenXR controller profile.
## <[osgXR/Manager](../include/osgXR/Manager)>
This header provides the ``osgXR::Manager`` class which an application can
extend to implement a VR manager class. Virtual callbacks tell the application
which camera views are required to implement VR, and an ``update()`` function
gives osgXR a chance to incrementally bring up or tear down VR.
## <[osgXR/Mirror](../include/osgXR/Mirror)>
This header provides the ``osgXR::Mirror`` class which an application can use to
register a desktop mirror of the VR view.
## <[osgXR/MirrorSettings](../include/osgXR/MirrorSettings)>
This header provides the ``osgXR::MirrorSettings`` class which encapsulates
configuration data for desktop mirrors of VR views.
## <[osgXR/OpenXRDisplay](../include/osgXR/OpenXRDisplay)>
This header provides the ``osgXR::OpenXRDisplay`` ViewConfig class. It is
largely replaced by ``osgXR::Manager``.
## <[osgXR/Settings](../include/osgXR/Settings)>
This header provides the ``osgXR::Settings`` class which encapsulates all the VR
configuration data.
## <[osgXR/Subaction](../include/osgXR/Subaction)>
This header provides the ``osgXR::Subaction`` class which an application can
use to represent OpenXR subaction paths (top level user paths), which allow a
single OpenXR action to represent the same action on both hands. It can be
passed to other action related classes to filter actions by hand, and it can be
extended by the application to implement a callback for InteractionProfile
changes.
## <[osgXR/View](../include/osgXR/View)>
This header provides the ``osgXR::View`` class which represents a view of the
world which ``osgXR::Manager`` will request to be configured by the application.
## <[osgXR/osgXR](../include/osgXR/osgXR)>
This header provides convenience functions for quick and easy integration of VR
capabilities into a simple OpenSceneGraph application or example. It is
recommended ``osgXR::Manager`` be used instead for most projects.
### osgXR::setupViewerDefaults()
This sets up VR on the provided viewer based on the content of the following
environment variables:
* ``OSGXR=1`` enables VR.
* ``OSGXR_MODE=SLAVE_CAMERAS`` forces the use of separate slave cameras per view.
* ``OSGXR_MODE=SCENE_VIEW`` forces the use of OpenSceneGraph's SceneView stereo (default).
* ``OSGXR_SWAPCHAIN=MULTIPLE`` forces the use of separate swapchains per view.
* ``OSGXR_SWAPCHAIN=SINGLE`` forces the use of a single swapchain containing all views.
* ``OSGXR_UNITS_PER_METER=10`` allows the scale of the environment to be controlled.
* ``OSGXR_VALIDATION_LAYER=1`` enables the OpenXR validation layer (off by default).
* ``OSGXR_DEPTH_INFO=1`` enables passing of depth information to OpenXR (off by default).
* ``OSGXR_MIRROR=NONE`` use a blank screen as the default mirror.
* ``OSGXR_MIRROR=LEFT`` use OpenXR view 0 (left) as the default mirror.
* ``OSGXR_MIRROR=RIGHT`` use OpenXR view 1 (right) as the default mirror.
* ``OSGXR_MIRROR=LEFT_RIGHT`` use both left and right views side by side as the default mirror.

29
3rdparty/osgXR/examples/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.11)
project(osgXR::examples)
find_package(OpenGL REQUIRED)
find_package(OpenSceneGraph REQUIRED COMPONENTS osgDB osgViewer)
if(CMAKE_PROJECT_NAME STREQUAL osgXR)
# If we're building from osgXR source tree, take a shortcut
set(osgXR_INCLUDE_DIR "../include")
set(osgXR_LIBRARY osgXR)
else()
# Otherwise, we'd normally use osgXR::osgXR, but osgXR_LIBRARY will do here
find_package(osgXR REQUIRED)
endif()
add_executable(osgteapot osgteapot.cpp)
target_include_directories(osgteapot
PRIVATE
${OPENGL_INCLUDE_DIR}
${OPENSCENEGRAPH_INCLUDE_DIRS}
)
target_link_libraries(osgteapot
PUBLIC
${OPENGL_LIBRARIES}
${OPENSCENEGRAPH_LIBRARIES}
${osgXR_LIBRARY}
)

365
3rdparty/osgXR/examples/osgteapot.cpp vendored Normal file
View file

@ -0,0 +1,365 @@
/* OpenSceneGraph example, osgteapot.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <osg/Geode>
#include <osg/TexGen>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgXR/osgXR>
// The classic OpenGL teapot... taken form glut-3.7/lib/glut/glut_teapot.c
/* Copyright (c) Mark J. Kilgard, 1994. */
/**
(c) Copyright 1993, Silicon Graphics, Inc.
ALL RIGHTS RESERVED
Permission to use, copy, modify, and distribute this software
for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that
both the copyright notice and this permission notice appear in
supporting documentation, and that the name of Silicon
Graphics, Inc. not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission.
THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU
"AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR
OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO
EVENT SHALL SILICON GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE
ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,
INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE,
SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR
NOT SILICON GRAPHICS, INC. HAS BEEN ADVISED OF THE POSSIBILITY
OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE OR
PERFORMANCE OF THIS SOFTWARE.
US Government Users Restricted Rights
Use, duplication, or disclosure by the Government is subject to
restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
(c)(1)(ii) of the Rights in Technical Data and Computer
Software clause at DFARS 252.227-7013 and/or in similar or
successor clauses in the FAR or the DOD or NASA FAR
Supplement. Unpublished-- rights reserved under the copyright
laws of the United States. Contractor/manufacturer is Silicon
Graphics, Inc., 2011 N. Shoreline Blvd., Mountain View, CA
94039-7311.
OpenGL(TM) is a trademark of Silicon Graphics, Inc.
*/
/* Rim, body, lid, and bottom data must be reflected in x and
y; handle and spout data across the y axis only. */
static int patchdata[][16] =
{
/* rim */
{102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15},
/* body */
{12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27},
{24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40},
/* lid */
{96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101,
101, 0, 1, 2, 3,},
{0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117},
/* bottom */
{118, 118, 118, 118, 124, 122, 119, 121, 123, 126,
125, 120, 40, 39, 38, 37},
/* handle */
{41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
53, 54, 55, 56},
{53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
28, 65, 66, 67},
/* spout */
{68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83},
{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95}
};
/* *INDENT-OFF* */
static float cpdata[][3] =
{
{0.2, 0, 2.7}, {0.2, -0.112, 2.7}, {0.112, -0.2, 2.7}, {0,
-0.2, 2.7}, {1.3375, 0, 2.53125}, {1.3375, -0.749, 2.53125},
{0.749, -1.3375, 2.53125}, {0, -1.3375, 2.53125}, {1.4375,
0, 2.53125}, {1.4375, -0.805, 2.53125}, {0.805, -1.4375,
2.53125}, {0, -1.4375, 2.53125}, {1.5, 0, 2.4}, {1.5, -0.84,
2.4}, {0.84, -1.5, 2.4}, {0, -1.5, 2.4}, {1.75, 0, 1.875},
{1.75, -0.98, 1.875}, {0.98, -1.75, 1.875}, {0, -1.75,
1.875}, {2, 0, 1.35}, {2, -1.12, 1.35}, {1.12, -2, 1.35},
{0, -2, 1.35}, {2, 0, 0.9}, {2, -1.12, 0.9}, {1.12, -2,
0.9}, {0, -2, 0.9}, {-2, 0, 0.9}, {2, 0, 0.45}, {2, -1.12,
0.45}, {1.12, -2, 0.45}, {0, -2, 0.45}, {1.5, 0, 0.225},
{1.5, -0.84, 0.225}, {0.84, -1.5, 0.225}, {0, -1.5, 0.225},
{1.5, 0, 0.15}, {1.5, -0.84, 0.15}, {0.84, -1.5, 0.15}, {0,
-1.5, 0.15}, {-1.6, 0, 2.025}, {-1.6, -0.3, 2.025}, {-1.5,
-0.3, 2.25}, {-1.5, 0, 2.25}, {-2.3, 0, 2.025}, {-2.3, -0.3,
2.025}, {-2.5, -0.3, 2.25}, {-2.5, 0, 2.25}, {-2.7, 0,
2.025}, {-2.7, -0.3, 2.025}, {-3, -0.3, 2.25}, {-3, 0,
2.25}, {-2.7, 0, 1.8}, {-2.7, -0.3, 1.8}, {-3, -0.3, 1.8},
{-3, 0, 1.8}, {-2.7, 0, 1.575}, {-2.7, -0.3, 1.575}, {-3,
-0.3, 1.35}, {-3, 0, 1.35}, {-2.5, 0, 1.125}, {-2.5, -0.3,
1.125}, {-2.65, -0.3, 0.9375}, {-2.65, 0, 0.9375}, {-2,
-0.3, 0.9}, {-1.9, -0.3, 0.6}, {-1.9, 0, 0.6}, {1.7, 0,
1.425}, {1.7, -0.66, 1.425}, {1.7, -0.66, 0.6}, {1.7, 0,
0.6}, {2.6, 0, 1.425}, {2.6, -0.66, 1.425}, {3.1, -0.66,
0.825}, {3.1, 0, 0.825}, {2.3, 0, 2.1}, {2.3, -0.25, 2.1},
{2.4, -0.25, 2.025}, {2.4, 0, 2.025}, {2.7, 0, 2.4}, {2.7,
-0.25, 2.4}, {3.3, -0.25, 2.4}, {3.3, 0, 2.4}, {2.8, 0,
2.475}, {2.8, -0.25, 2.475}, {3.525, -0.25, 2.49375},
{3.525, 0, 2.49375}, {2.9, 0, 2.475}, {2.9, -0.15, 2.475},
{3.45, -0.15, 2.5125}, {3.45, 0, 2.5125}, {2.8, 0, 2.4},
{2.8, -0.15, 2.4}, {3.2, -0.15, 2.4}, {3.2, 0, 2.4}, {0, 0,
3.15}, {0.8, 0, 3.15}, {0.8, -0.45, 3.15}, {0.45, -0.8,
3.15}, {0, -0.8, 3.15}, {0, 0, 2.85}, {1.4, 0, 2.4}, {1.4,
-0.784, 2.4}, {0.784, -1.4, 2.4}, {0, -1.4, 2.4}, {0.4, 0,
2.55}, {0.4, -0.224, 2.55}, {0.224, -0.4, 2.55}, {0, -0.4,
2.55}, {1.3, 0, 2.55}, {1.3, -0.728, 2.55}, {0.728, -1.3,
2.55}, {0, -1.3, 2.55}, {1.3, 0, 2.4}, {1.3, -0.728, 2.4},
{0.728, -1.3, 2.4}, {0, -1.3, 2.4}, {0, 0, 0}, {1.425,
-0.798, 0}, {1.5, 0, 0.075}, {1.425, 0, 0}, {0.798, -1.425,
0}, {0, -1.5, 0.075}, {0, -1.425, 0}, {1.5, -0.84, 0.075},
{0.84, -1.5, 0.075}
};
static float tex[2][2][2] =
{
{ {0, 0},
{1, 0}},
{ {0, 1},
{1, 1}}
};
/* *INDENT-ON* */
static void
teapot(GLint grid, GLenum type)
{
float p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3];
long i, j, k, l;
glPushAttrib(GL_ENABLE_BIT | GL_EVAL_BIT);
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glEnable(GL_MAP2_VERTEX_3);
glEnable(GL_MAP2_TEXTURE_COORD_2);
for (i = 0; i < 10; i++) {
for (j = 0; j < 4; j++) {
for (k = 0; k < 4; k++) {
for (l = 0; l < 3; l++) {
p[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
q[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l];
if (l == 1)
q[j][k][l] *= -1.0;
if (i < 6) {
r[j][k][l] =
cpdata[patchdata[i][j * 4 + (3 - k)]][l];
if (l == 0)
r[j][k][l] *= -1.0;
s[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
if (l == 0)
s[j][k][l] *= -1.0;
if (l == 1)
s[j][k][l] *= -1.0;
}
}
}
}
glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2,
&tex[0][0][0]);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4,
&p[0][0][0]);
glMapGrid2f(grid, 0.0, 1.0, grid, 0.0, 1.0);
glEvalMesh2(type, 0, grid, 0, grid);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4,
&q[0][0][0]);
glEvalMesh2(type, 0, grid, 0, grid);
if (i < 6) {
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4,
&r[0][0][0]);
glEvalMesh2(type, 0, grid, 0, grid);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4,
&s[0][0][0]);
glEvalMesh2(type, 0, grid, 0, grid);
}
}
glPopAttrib();
}
// Now the OSG wrapper for the above OpenGL code, the most complicated bit is computing
// the bounding box for the above example, normally you'll find this the easy bit.
class Teapot : public osg::Drawable
{
public:
Teapot() {}
/** Copy constructor using CopyOp to manage deep vs shallow copy.*/
Teapot(const Teapot& teapot,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):
osg::Drawable(teapot,copyop) {}
META_Object(myTeapotApp,Teapot)
// the draw immediate mode method is where the OSG wraps up the drawing of
// of OpenGL primitives.
virtual void drawImplementation(osg::RenderInfo&) const
{
// teapot(..) doesn't use vertex arrays at all so we don't need to toggle their state
// if we did we'd need to something like following call
// state.disableAllVertexArrays(), see src/osg/Geometry.cpp for the low down.
// just call the OpenGL code.
teapot(14,GL_FILL);
}
// we need to set up the bounding box of the data too, so that the scene graph knows where this
// objects is, for both positioning the camera at start up, and most importantly for culling.
virtual osg::BoundingBox computeBoundingBox() const
{
osg::BoundingBox bbox;
// follow is some truly horrible code required to calculate the
// bounding box of the teapot. Have used the original code above to do
// help compute it.
float p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3];
long i, j, k, l;
for (i = 0; i < 10; i++) {
for (j = 0; j < 4; j++) {
for (k = 0; k < 4; k++) {
for (l = 0; l < 3; l++) {
p[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
q[j][k][l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l];
if (l == 1)
q[j][k][l] *= -1.0;
if (i < 6) {
r[j][k][l] =
cpdata[patchdata[i][j * 4 + (3 - k)]][l];
if (l == 0)
r[j][k][l] *= -1.0;
s[j][k][l] = cpdata[patchdata[i][j * 4 + k]][l];
if (l == 0)
s[j][k][l] *= -1.0;
if (l == 1)
s[j][k][l] *= -1.0;
}
}
bbox.expandBy(osg::Vec3(p[j][k][0],p[j][k][1],p[j][k][2]));
bbox.expandBy(osg::Vec3(q[j][k][0],q[j][k][1],q[j][k][2]));
if (i < 6)
{
bbox.expandBy(osg::Vec3(r[j][k][0],r[j][k][1],r[j][k][2]));
bbox.expandBy(osg::Vec3(s[j][k][0],s[j][k][1],s[j][k][2]));
}
}
}
}
return bbox;
}
protected:
virtual ~Teapot() {}
};
osg::Geode* createTeapot()
{
osg::Geode* geode = new osg::Geode();
// add the teapot to the geode.
geode->addDrawable( new Teapot );
// add a reflection map to the teapot.
osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile("Images/reflect.rgb");
if (image)
{
osg::Texture2D* texture = new osg::Texture2D;
texture->setImage(image);
osg::TexGen* texgen = new osg::TexGen;
texgen->setMode(osg::TexGen::SPHERE_MAP);
osg::StateSet* stateset = new osg::StateSet;
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(0,texgen,osg::StateAttribute::ON);
geode->setStateSet(stateset);
}
return geode;
}
int main(int , char **)
{
#if 1
// create viewer on heap as a test, this looks to be causing problems
// on init on some platforms, and seg fault on exit when multi-threading on linux.
// Normal stack based version below works fine though...
// construct the viewer.
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
// add model to viewer.
viewer->setSceneData( createTeapot() );
osgXR::setupViewerDefaults(viewer, "osgteaport", 1);
return viewer->run();
#else
// construct the viewer.
osgViewer::Viewer viewer;
// add model to viewer.
viewer.setSceneData( createTeapot() );
// create the windows and run the threads.
return viewer.run();
#endif
}

471
3rdparty/osgXR/include/osgXR/Action vendored Normal file
View file

@ -0,0 +1,471 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_Action
#define OSGXR_Action 1
#include <osgXR/Export>
#include <osg/Quat>
#include <osg/Referenced>
#include <osg/Vec2f>
#include <osg/Vec3f>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
namespace osgXR {
class ActionSet;
class Subaction;
/**
* Represents an OpenXR action.
* OpenXR actions are inputs & outputs which are abstracted from the physical
* input sources. The OpenXR runtime is responsible for binding them to sources,
* using suggested bindings in interaction profiles.
*
* These Action objects can persist across multiple VR sessions, and changes can
* be made at any time, however some changes won't take effect while a session
* is running.
*/
class OSGXR_EXPORT Action : public osg::Referenced
{
public:
class Private;
protected:
/// Constructor (internal).
Action(Private *priv);
public:
/// Destructor.
~Action();
/**
* Add a subaction that may be later queried.
* Any subaction that is intended to be queried must be added to the
* action first.
* @param subaction Subaction that may be later queried.
*/
void addSubaction(Subaction *subaction);
// Accessors
/**
* Set the action's name and localized name.
* @param name New name for OpenXR action.
* @param localizedName A localized version of @p name.
*/
void setName(const std::string &name,
const std::string &localizedName);
/**
* Set the action's name.
* @param name New name for OpenXR action.
*/
void setName(const std::string &name);
/// Get the action's name.
const std::string &getName() const;
/**
* Set the action's localized name.
* @param localizedName The localized name for the action.
*/
void setLocalizedName(const std::string &localizedName);
/// Get the action's localized name.
const std::string &getLocalizedName() const;
/**
* 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;
typedef enum {
// Must match XR_INPUT_SOURCE_LOCALIZED_NAME_*
/// Include user path (e.g. "Left Hand").
USER_PATH_BIT = 1,
/// Include interaction profile (e.g. "Vive Controller").
INTERACTION_PROFILE_BIT = 2,
/// Include input component (e.g. "Trigger").
COMPONENT_BIT = 4,
} LocalizedNameFlags;
/**
* 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(uint32_t whichComponents,
std::vector<std::string> &names) const;
private:
std::unique_ptr<Private> _private;
// Copying not permitted
Action(const Action &copy);
};
/// An action that can only have boolean values.
class OSGXR_EXPORT ActionBoolean : public Action
{
public:
/**
* Construct a boolean action.
* @param actionSet The action set the action should belong to.
*/
ActionBoolean(ActionSet *actionSet);
/**
* Construct a boolean action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action, also used as the
* localized name.
*/
ActionBoolean(ActionSet *actionSet,
const std::string &name);
/**
* Construct a boolean action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action.
* @param localizedName The localized name for the action.
*/
ActionBoolean(ActionSet *actionSet,
const std::string &name,
const std::string &localizedName);
/**
* Get the current value of the action as a bool.
* @param subaction The subaction to filter sources from, which must
* have been specified to Action::addSubaction().
* @return The current value of the action.
*/
bool getValue(Subaction *subaction = nullptr);
};
/// An action that can have floating point values.
class OSGXR_EXPORT ActionFloat : public Action
{
public:
/**
* Construct a floating-point action.
* @param actionSet The action set the action should belong to.
*/
ActionFloat(ActionSet *actionSet);
/**
* Construct a floating-point action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action, also used as the
* localized name.
*/
ActionFloat(ActionSet *actionSet,
const std::string &name);
/**
* Construct a floating-point action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action.
* @param localizedName The localized name for the action.
*/
ActionFloat(ActionSet *actionSet,
const std::string &name,
const std::string &localizedName);
/**
* Get the current value of the action as a float.
* @param subaction The subaction to filter sources from, which must
* have been specified to Action::addSubaction().
* @return The current value of the action.
*/
float getValue(Subaction *subaction = nullptr);
};
/// An action that can have 2 dimentional floating point vector values.
class OSGXR_EXPORT ActionVector2f : public Action
{
public:
/**
* Construct a 2d floating-point vector action.
* @param actionSet The action set the action should belong to.
*/
ActionVector2f(ActionSet *actionSet);
/**
* Construct a 2d floating-point vector action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action, also used as the
* localized name.
*/
ActionVector2f(ActionSet *actionSet,
const std::string &name);
/**
* Construct a 2d floating-point vector action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action.
* @param localizedName The localized name for the action.
*/
ActionVector2f(ActionSet *actionSet,
const std::string &name,
const std::string &localizedName);
/**
* Get the current value of the action as an OSG vector.
* @param subaction The subaction to filter sources from, which must
* have been specified to Action::addSubaction().
* @return The current value of the action.
*/
osg::Vec2f getValue(Subaction *subaction = nullptr);
};
/// An action that can have pose (position and orientation) values.
class OSGXR_EXPORT ActionPose : public Action
{
public:
/**
* Construct a pose action.
* @param actionSet The action set the action should belong to.
*/
ActionPose(ActionSet *actionSet);
/**
* Construct a pose action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action, also used as the
* localized name.
*/
ActionPose(ActionSet *actionSet,
const std::string &name);
/**
* Construct a pose action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action.
* @param localizedName The localized name for the action.
*/
ActionPose(ActionSet *actionSet,
const std::string &name,
const std::string &localizedName);
/**
* Represents a pose action's position and orientation.
* This represents a pose action's position and orientation, along with
* flags to indicate whether each of these are valid and whether they're
* currently tracked (as opposed to estimated based on recent tracking).
*/
class OSGXR_EXPORT Location
{
public:
typedef enum {
// Must match XR_SPACE_LOCATION_* */
ORIENTATION_VALID_BIT = 0x1,
POSITION_VALID_BIT = 0x2,
ORIENTATION_TRACKED_BIT = 0x4,
POSITION_TRACKED_BIT = 0x8,
} Flags;
// Constructors
/// Construct a pose action location.
Location();
/// Construct a pose action location.
Location(Flags flags,
const osg::Quat &orientation,
const osg::Vec3f &position);
// Accessors
/**
* Find whether the orientation is valid.
* If not, the orientation is undefined.
* @return Whether the orientation is valid.
*/
bool isOrientationValid() const
{
return _flags & ORIENTATION_VALID_BIT;
}
/**
* Find whether the position is valid.
* If not, the position is undefined.
* @return Whether the position is valid.
*/
bool isPositionValid() const
{
return _flags & POSITION_VALID_BIT;
}
/**
* Find whether the orientation is being tracked.
* If not, the orientation may only be an estimate.
* @return Whether the orientation is being tracked.
*/
bool isOrientationTracked() const
{
return _flags & ORIENTATION_TRACKED_BIT;
}
/**
* Find whether the position is being tracked.
* If not, the position may only be an estimate.
* @return Whether the position is being tracked.
*/
bool isPositionTracked() const
{
return _flags & POSITION_TRACKED_BIT;
}
/// Get the flags which indicate validity and tracking.
Flags getFlags() const
{
return _flags;
}
/**
* Get the pose action's orientation as a quaternion.
* Get the pose action's orientation relative to the default
* reference space as an OSG quaternion.
*
* The orientation is undefined if isOrientationValid() returns
* false.
*
* The orientation may only be an estimate if
* isOrientationTracked() returns false.
*/
const osg::Quat &getOrientation() const
{
return _orientation;
}
/**
* Get the pose action's position as a 3D vector.
* Get the pose action's position relative to the default
* reference space as an OSG 3D vector.
*
* The position is undefined if isPositionValid() returns false.
*
* The position may only be an estimate if isPositionTracked()
* returns false.
*/
const osg::Vec3f &getPosition() const
{
return _position;
}
// Comparison operators
bool operator != (const Location &other) const
{
return _flags != other._flags ||
(isOrientationValid() && _orientation != other._orientation) ||
(isPositionValid() && _position != other._position);
}
bool operator == (const Location &other) const
{
return !operator != (other);
}
protected:
Flags _flags;
osg::Quat _orientation;
osg::Vec3f _position;
};
/**
* Get the current pose of the action as a Location object.
* @param subaction The subaction to filter sources from, which must
* have been specified to Action::addSubaction().
* @return The current pose of the action.
*/
Location getValue(Subaction *subaction = nullptr);
};
/// An output action for vibration.
class OSGXR_EXPORT ActionVibration : public Action
{
public:
/**
* Construct a vibration output action.
* @param actionSet The action set the action should belong to.
*/
ActionVibration(ActionSet *actionSet);
/**
* Construct a vibration output action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action, also used as the
* localized name.
*/
ActionVibration(ActionSet *actionSet,
const std::string &name);
/**
* Construct a vibration output action.
* @param actionSet The action set the action should belong to.
* @param name The name of the OpenXR action.
* @param localizedName The localized name for the action.
*/
ActionVibration(ActionSet *actionSet,
const std::string &name,
const std::string &localizedName);
enum {
/// Indicates a minimum supported durection for a haptic device.
DURATION_MIN = -1,
/// Indicates an optimal frequency for a haptic pulse.
FREQUENCY_UNSPECIFIED = 0,
};
/**
* Apply haptic feedback.
* @param duration_ns Duration of vibration in nanoseconds.
* @param frequency Frequency of vibration in Hz.
* @param amplitude Amplitude of vibration between 0.0 and 1.0.
* @return true on success, false otherwise.
*/
bool applyHapticFeedback(int64_t duration_ns, float frequency,
float amplitude);
/**
* Apply haptic feedback.
* @param subaction The subaction to apply haptics to, which must
* have been specified to Action::addSubaction().
* @param duration_ns Duration of vibration in nanoseconds.
* @param frequency Frequency of vibration in Hz.
* @param amplitude Amplitude of vibration between 0.0 and 1.0.
* @return true on success, false otherwise.
*/
bool applyHapticFeedback(Subaction *subaction,
int64_t duration_ns, float frequency,
float amplitude);
/**
* Stop any in-progress haptic feedback.
* @param subaction The subaction to apply haptics to, which must
* have been specified to Action::addSubaction().
* @return true on success, false otherwise.
*/
bool stopHapticFeedback(Subaction *subaction = nullptr);
};
}
#endif

138
3rdparty/osgXR/include/osgXR/ActionSet vendored Normal file
View file

@ -0,0 +1,138 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_ActionSet
#define OSGXR_ActionSet 1
#include <osgXR/Export>
#include <osg/Referenced>
#include <cstdint>
#include <memory>
#include <string>
namespace osgXR {
class Manager;
class Subaction;
/**
* Represents a group of OpenXR actions.
* Action sets are attached to the OpenXR session, and can be dynamically
* activated and deactivated.
*/
class OSGXR_EXPORT ActionSet : public osg::Referenced
{
public:
/**
* Construct an action set.
* @param manager The VR manager object to add the action set to.
*/
ActionSet(Manager *manager);
/**
* Construct an action set.
* @param manager The VR manager object to add the action set to.
* @param name The name of the OpenXR action set, also used as the
* localized name.
*/
ActionSet(Manager *manager,
const std::string &name);
/**
* Construct an action set.
* @param manager The VR manager object to add the action set to.
* @param name The name of the OpenXR action set.
* @param localizedName The localized name for the action set.
*/
ActionSet(Manager *manager,
const std::string &name,
const std::string &localizedName);
/// Destructor.
~ActionSet();
// Accessors
/**
* Set the action set's name and localized name.
* @param name New name for OpenXR action set.
* @param localizedName A localized version of @p name.
*/
void setName(const std::string &name,
const std::string &localizedName);
/**
* Set the action set's name.
* @param name New name for OpenXR action set.
*/
void setName(const std::string &name);
/// Get the action's name.
const std::string &getName() const;
/**
* Set the action set's localized name.
* @param localizedName The localized name for the action set.
*/
void setLocalizedName(const std::string &localizedName);
/// Get the action set's localized name.
const std::string &getLocalizedName() const;
/**
* Set the priority of the action set.
* @param priority New priority of the action set. Larger priority
* action sets take precedence over smaller priority
* action sets.
*/
void setPriority(uint32_t priority);
/// Get the priority of the action set.
uint32_t getPriority() const;
// Activation of the action set
/**
* Activate the action set within a subaction.
* Set the action set as active so that its actions (filtered by
* subaction) are synchronised each frame. If @p subaction is nullptr,
* all subactions in the set will be synchronised, otherwise multiple
* subactions can be activated by multiple calls.
* @param subaction The subaction to activate this action set within.
* May be nullptr (default) in which case all
* subactions are activated.
*/
void activate(Subaction *subaction = nullptr);
/**
* Deactivate the action set within a subaction.
* Set the action set as inactive so that its actions (filtered by
* subaction) are no longer synchronised each frame. If @p subaction is
* nullptr, any full activation is removed, otherwise multiple
* subactions can be deactivated by multiple calls.
* @param subaction The subaction to deactivate this action set within.
* May be nullptr (default) in which case all
* subactions activations are removed.
*/
void deactivate(Subaction *subaction = nullptr);
/**
* Find whether the action set is activated for any subactions.
* @return Whether any subactions are activated for this action set.
*/
bool isActive();
class Private;
private:
std::unique_ptr<Private> _private;
// Copying not permitted
ActionSet(const ActionSet &copy);
};
}
#endif

22
3rdparty/osgXR/include/osgXR/Export vendored Normal file
View file

@ -0,0 +1,22 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_Export
#define OSGXR_Export 1
#include <osgXR/Config>
#if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__)
#if defined(OSGXR_STATIC_LIBRARY)
#define OSGXR_EXPORT
#elif defined(OSGXR_LIBRARY)
#define OSGXR_EXPORT __declspec(dllexport)
#else
#define OSGXR_EXPORT __declspec(dllimport)
#endif
#else
#define OSGXR_EXPORT
#endif
#endif

View file

@ -0,0 +1,74 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_InteractionProfile
#define OSGXR_InteractionProfile 1
#include <osgXR/Export>
#include <osg/Referenced>
#include <memory>
#include <string>
namespace osgXR {
class Action;
class Manager;
/**
* Represents a group of suggested bindings for a specific interaction profile.
* This class allow the application to suggest bindings for actions to specific
* input paths for a given interaction profile. If the OpenXR runtime recognises
* the profile it may use the suggested bindings to bind actions to whichever
* input devices the user may have, even without a specific binding to that
* device.
*/
class OSGXR_EXPORT InteractionProfile : public osg::Referenced
{
public:
/**
* Construct an interaction profile.
* The OpenXR interaction profile path is constructed as
* "/interaction_profiles/@p vendor /@p type ".
* @param manager The VR manager object to add the action set to.
* @param vendor Vendor segment of OpenXR interaction profile path.
* @param type Type segment of OpenXR interaction profile path.
*/
InteractionProfile(Manager *manager,
const std::string &vendor,
const std::string &type);
/// Destructor
~InteractionProfile();
// Accessors
/// Get the vendor segment of the OpenXR interaction profile path.
const std::string &getVendor() const;
/// Get the type segment of the OpenXR interaction profile path.
const std::string &getType() const;
/**
* Suggest a binding for an action.
* @param action The action to bind.
* @param binding The OpenXR path to bind the action to.
*/
void suggestBinding(Action *action, const std::string &binding);
class Private;
private:
std::unique_ptr<Private> _private;
// Copying not permitted
InteractionProfile(const InteractionProfile &copy);
};
}
#endif

237
3rdparty/osgXR/include/osgXR/Manager vendored Normal file
View file

@ -0,0 +1,237 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_Manager
#define OSGXR_Manager 1
#include <osg/Camera>
#include <osg/Node>
#include <osg/ref_ptr>
#include <osgViewer/View>
#include <osgViewer/ViewerBase>
#include <osgXR/Export>
#include <osgXR/Mirror>
#include <osgXR/Settings>
#include <osgXR/View>
#include <list>
namespace osgXR {
// Internal state class
class XRState;
/**
* Public VR state manager class.
* Applications can extend this class to allow tighter integration with osgXR.
*/
class OSGXR_EXPORT Manager : public osgViewer::ViewConfig
{
public:
Manager();
virtual ~Manager();
/// Use if viewer is a CompositeViewer.
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) const;
void configure(osgViewer::View& view) const override;
/**
* Perform a regular update.
* This will poll for OpenXR events, and handle any pending VR start /
* stop operations (possibly invoking the Manager's view callbacks).
* Some of these operations require threading on the viewer to be
* temporarily stopped, but in all cases it is started again.
*/
virtual void update();
/// Find whether state has changed since last call, and reset.
bool checkAndResetStateChanged();
/// Find whether VR seems to be present.
bool getPresent() const;
/**
* Get whether VR is currently set to be enabled.
* When enabled, osgXR will try to keep VR running.
* @return Whether VR is enabled
*/
bool getEnabled() const;
/**
* Set whether VR is currently set to be enabled.
* When enabled, osgXR will try to keep VR running.
* @param enabled Whether VR is enabled.
*/
void setEnabled(bool enabled);
/**
* Start destroying the VR state and wait for safe shutdown.
*/
void destroyAndWait();
/**
* Find whether this manager is in the process of being destroyed.
*/
bool isDestroying() const;
/**
* Get whether a VR session is currently running.
* @return Whether a VR session is currently running.
*/
bool isRunning() const;
/// Arrange reinit as needed for new settings.
void syncSettings();
/// Arrange reinit as needed of action setup.
void syncActionSetup();
/*
* OpenXR information.
*/
/**
* Find whether OpenXR's validation layer is supported.
* This looks to see whether the OpenXR validation API layer (i.e.
* XR_APILAYER_LUNARG_core_validation) is available.
*/
bool hasValidationLayer() const;
/**
* Find whether OpenXR supports the submission of depth information.
* This looks to see whether the OpenXR instance extension for
* submitting depth information to help the runtime perform better
* reprojection (i.e. XR_KHR_composition_layer_depth) is available.
*/
bool hasDepthInfoExtension() const;
/**
* Find whether OpenXR supports the visibility mask extension.
* This looks to see whether the OpenXR instance extension for getting
* visibility masks is available, which can be used to reduce fragment
* load.
*/
bool hasVisibilityMaskExtension() const;
/// Find the name of the OpenXR runtime.
const char *getRuntimeName() const;
/// Find the name of the OpenXR system in use.
const char *getSystemName() const;
/// Get a string describing the state (for user consumption).
const char *getStateString() const;
/*
* For implementation by derived classes.
*/
/**
* Callback telling the app to configure a new view.
* This callback allows osgXR to tell the app to configure a new view of
* the world. The application should notify osgXR of the addition and
* removal of slave cameras which osgXR should hook into using the
* osgXR::View parameter.
* The implementation may stop threading, and it will be started again
* before update() returns.
* @param xrView The new osgXR::View with a public API to allow the
* application to retrieve what it needs in relation to
* the view and to inform osgXR of changes.
*/
virtual void doCreateView(View *xrView) = 0;
/**
* Callback telling the app to destroy an existing view.
* This callback allows osgXR to tell the app to remove an existing view
* of the world that it had requested via doCreateView(). The
* application should notify osgXR of the removal of any slave cameras
* which it has already informed osgXR about.
* The implementation may stop threading, and it will be started again
* before update() returns.
*/
virtual void doDestroyView(View *xrView) = 0;
/**
* Callback telling the app that the VR session is now running.
* This happens after the OpenXR session has started running, and views
* have been configured (see doCreateView()). The app should start
* rendering the VR views, and may choose to reconfigure the desktop
* window to make a VR mirror visible.
*/
virtual void onRunning();
/**
* Callback telling the app that the VR session has now stopped.
* This happens after the OpenXR session has stopped, and views have
* been removed (see doDestroyView()). The app should stop rendering the
* VR views, and may choose to reconfigure the desktop window so as to
* no longer show a VR mirror.
*/
virtual void onStopped();
/**
* Callback telling the app that the VR session is in focus.
* This happens when the VR session enters focus and can get VR input
* from the user. The app may choose to resume the experience if it was
* previously paused due to onUnfocus().
*/
virtual void onFocus();
/**
* Callback telling the app that the VR session is no longer in focus.
* This happens when the VR session leaves focus and can no longer get
* VR input from the user. The VR runtime may be presenting a modal
* pop-up on top of the application's rendered frames. The app may
* choose to pause the experience.
*/
virtual void onUnfocus();
/// Add a custom mirror to the queue of mirrors to configure.
void addMirror(Mirror *mirror);
/// Set up a camera to render a VR mirror.
void setupMirrorCamera(osg::Camera *camera);
/*
* Internal
*/
inline Settings *_getSettings()
{
return _settings.get();
}
inline XRState *_getXrState()
{
return _state;
}
void _setupMirrors();
protected:
osg::ref_ptr<osgViewer::ViewerBase> _viewer;
osg::ref_ptr<Settings> _settings;
bool _destroying;
private:
std::list<osg::ref_ptr<Mirror> > _mirrorQueue;
osg::ref_ptr<XRState> _state;
};
}
#endif

50
3rdparty/osgXR/include/osgXR/Mirror vendored Normal file
View file

@ -0,0 +1,50 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_Mirror
#define OSGXR_Mirror 1
#include <osg/Camera>
#include <osg/Referenced>
#include <osg/ref_ptr>
#include <osgXR/Export>
#include <osgXR/MirrorSettings>
namespace osgXR {
class Manager;
/**
* Public VR mirror class.
*/
class OSGXR_EXPORT Mirror : public osg::Referenced
{
public:
Mirror(Manager *manager, osg::Camera *camera);
virtual ~Mirror();
/*
* internal
*/
// Called when enough is known about OpenXR system
void _init();
private:
void setupQuad(unsigned int viewIndex,
float x, float w);
osg::observer_ptr<Manager> _manager;
osg::observer_ptr<osg::Camera> _camera;
MirrorSettings _mirrorSettings;
};
}
#endif

View file

@ -0,0 +1,72 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_MirrorSettings
#define OSGXR_MirrorSettings 1
#include <osgXR/Export>
namespace osgXR {
class OSGXR_EXPORT MirrorSettings
{
public:
MirrorSettings();
/// Equality operator.
bool operator == (const MirrorSettings &other) const
{
return _mirrorMode == other._mirrorMode &&
(_mirrorMode != MIRROR_SINGLE ||
_mirrorViewIndex == other._mirrorViewIndex);
}
/// Inequality operator.
bool operator != (const MirrorSettings &other) const
{
return _mirrorMode != other._mirrorMode ||
(_mirrorMode == MIRROR_SINGLE &&
_mirrorViewIndex != other._mirrorViewIndex);
}
/// Type of VR mirror to show.
typedef enum MirrorMode
{
/// Choose automatically.
MIRROR_AUTOMATIC,
/// Render nothing to the mirror.
MIRROR_NONE,
/// Render a single view fullscreen to the mirror.
MIRROR_SINGLE,
/// Render left & right views side by side.
MIRROR_LEFT_RIGHT,
} MirrorMode;
/// Set the mirror mode to use.
void setMirror(MirrorMode mode, int viewIndex = -1)
{
_mirrorMode = mode;
_mirrorViewIndex = viewIndex;
}
/// Get the mirror mode to use.
MirrorMode getMirrorMode() const
{
return _mirrorMode;
}
/// Get the mirror view index.
int getMirrorViewIndex() const
{
return _mirrorViewIndex;
}
protected:
// Mirror mode
MirrorMode _mirrorMode;
int _mirrorViewIndex;
};
}
#endif

View file

@ -0,0 +1,49 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_OpenXRDisplay
#define OSGXR_OpenXRDisplay 1
#include <osgXR/Export>
#include <osgXR/Settings>
#include <osg/Referenced>
#include <osg/ref_ptr>
#include <osgViewer/View>
#include <cinttypes>
#include <string>
namespace osgXR {
class XRState;
/** a camera for each OpenXR view.*/
class OSGXR_EXPORT OpenXRDisplay : public osgViewer::ViewConfig
{
public:
OpenXRDisplay();
OpenXRDisplay(Settings *settings);
OpenXRDisplay(const OpenXRDisplay& rhs,
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
virtual ~OpenXRDisplay();
META_Object(osgXR, OpenXRDisplay);
void configure(osgViewer::View& view) const override;
protected:
osg::ref_ptr<Settings> _settings;
// Internal OpenXR state object
// FIXME this should probably belong elsewhere
mutable osg::ref_ptr<XRState> _state;
};
}
#endif

345
3rdparty/osgXR/include/osgXR/Settings vendored Normal file
View file

@ -0,0 +1,345 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_Settings
#define OSGXR_Settings 1
#include <osg/Referenced>
#include <osgXR/Export>
#include <osgXR/MirrorSettings>
#include <string>
namespace osgXR {
/// Encapsulates osgXR / OpenXR settings data.
class OSGXR_EXPORT Settings : public osg::Referenced
{
public:
/*
* Instance management.
*/
Settings();
virtual ~Settings();
/// Get the default/global instance of Settings.
static Settings *instance();
/*
* OpenXR application information.
*/
/**
* Set the application's name and version to expose to OpenXR.
* These will be used to create an OpenXR instance.
* @param appName Name of the application.
* @param appVersion 32-bit version number of the application.
*/
void setApp(const std::string &appName, uint32_t appVersion)
{
_appName = appName;
_appVersion = appVersion;
}
/**
* Set the application's name to expose to OpenXR.
* This will be used to create an OpenXR instance.
* @param appName Name of the application.
*/
void setAppName(const std::string &appName)
{
_appName = appName;
}
/// Get the application's name to expose to OpenXR.
const std::string &getAppName() const
{
return _appName;
}
/**
* Set the application's version to expose to OpenXR.
* This will be used to create an OpenXR instance.
* @param appVersion 32-bit version number of the application.
*/
void setAppVersion(uint32_t appVersion)
{
_appVersion = appVersion;
}
/// Get the application's 32-bit version number to expose to OpenXR.
uint32_t getAppVersion() const
{
return _appVersion;
}
/*
* osgXR configuration settings.
*/
/**
* Set whether to try enabling OpenXR's validation layer.
* This controls whether the OpenXR validation API layer (i.e.
* XR_APILAYER_LUNARG_core_validation) will be enabled when creating an
* OpenXR instance.
* By default this is disabled.
* @param validationLayer Whether to try enabling the validation layer.
*/
void setValidationLayer(bool validationLayer)
{
_validationLayer = validationLayer;
}
/// Get whether to try enabling OpenXR's validation layer.
bool getValidationLayer() const
{
return _validationLayer;
}
/**
* Set whether to enable submission of depth information to OpenXR.
* This controls whether the OpenXR instance depth information extension
* (i.e XR_KHR_composition_layer_depth) will be used to submit depth
* information to OpenXR to allow improved reprojection.
* This is currently disabled by default.
* @param depthInfo Whether to enable submission of depth information.
*/
void setDepthInfo(bool depthInfo)
{
_depthInfo = depthInfo;
}
/// Get whether to enable submission of depth information to OpenXR.
bool getDepthInfo() const
{
return _depthInfo;
}
/**
* Set whether to create visibility masks.
* This controls whether the OpenXR instance visibility mask extension
* (i.e. XR_KHR_visibility_mask) will be used to create and update
* visibility masks for each VR view in order to mask hidden fragments.
* This is enabled by default.
* @param visibilityMask Whether to create visibility masks.
*/
void setVisibilityMask(bool visibilityMask)
{
_visibilityMask = visibilityMask;
}
/// Get whether to create visibility masks.
bool getVisibilityMask() const
{
return _visibilityMask;
}
/// OpenXR system orm factors.
typedef enum FormFactor
{
/// A display mounted to the user's head.
HEAD_MOUNTED_DISPLAY,
/// A display held in the user's hands.
HANDHELD_DISPLAY,
} FormFactor;
/**
* Set which OpenXR form factor to use.
* This controls which OpenXR form factor to try to use. The default is
* HEAD_MOUNTED_DISPLAY.
* @param formFactor Form factor to use.
*/
void setFormFactor(FormFactor formFactor)
{
_formFactor = formFactor;
}
/// Get which OpenXR form factor to use.
FormFactor getFormFactor() const
{
return _formFactor;
}
/// Modes for blending layers onto the user's view of the real world.
typedef enum BlendMode
{
// Matches XrEnvironmentBlendMode
/// Display layers with no view of physical world behind.
OPAQUE = 1,
/// Additively blend layers with view of physical world behind.
ADDITIVE = 2,
/// Alpha blend layers with view of physical world behind.
ALPHA_BLEND = 3,
} BlendMode;
/**
* Specify a preferred environment blend mode.
* The chosen environment blend mode is permitted for use, and will be
* chosen in preference to any other supported environment blend modes
* specified by allowEnvBlendMode() if supported by OpenXR.
* @param mode Environment blend mode to prefer.
*/
void preferEnvBlendMode(BlendMode mode)
{
uint32_t mask = (1u << (unsigned int)mode);
_preferredEnvBlendModeMask |= mask;
_allowedEnvBlendModeMask |= mask;
}
/**
* Specify a permitted environment blend mode.
* The chosen environment blend mode is permitted for use, and may be
* chosen if supported by OpenXR when none of the preferred environment
* blend modes specified by preferEnvBlenMode() are supported by OpenXR.
* @param mode Environment blend mode to prefer.
*/
void allowEnvBlendMode(BlendMode mode)
{
uint32_t mask = (1u << (unsigned int)mode);
_allowedEnvBlendModeMask |= mask;
}
/// Get the bitmask of preferred environment blend modes.
uint32_t getPreferredEnvBlendModeMask() const
{
return _preferredEnvBlendModeMask;
}
/// Set the bitmask of preferred environment blend modes.
void setPreferredEnvBlendModeMask(uint32_t preferredEnvBlendModeMask)
{
_preferredEnvBlendModeMask = preferredEnvBlendModeMask;
}
/// Get the bitmask of permitted environment blend modes.
uint32_t getAllowedEnvBlendModeMask() const
{
return _allowedEnvBlendModeMask;
}
/// Set the bitmask of allowed environment blend modes.
void setAllowedEnvBlendModeMask(uint32_t allowedEnvBlendModeMask)
{
_allowedEnvBlendModeMask = allowedEnvBlendModeMask;
}
/// Techniques for rendering multiple views.
typedef enum VRMode
{
/// Choose automatically.
VRMODE_AUTOMATIC,
/** Create a slave camera for each view.
* Either separate swapchains, or single with multiple viewports.
*/
VRMODE_SLAVE_CAMERAS,
/** Use the OSG SceneView stereo functionality.
* No extra slave cameras.
* Only supports SWAPCHAIN_SINGLE with stereo.
*/
VRMODE_SCENE_VIEW,
} VRMode;
/// Set the rendering technique to use.
void setVRMode(VRMode mode)
{
_vrMode = mode;
}
/// Get the rendering technique to use.
VRMode getVRMode() const
{
return _vrMode;
}
/// Techniques for managing swapchains.
typedef enum SwapchainMode
{
/// Choose automatically.
SWAPCHAIN_AUTOMATIC,
/// Create a 2D swapchain per view.
SWAPCHAIN_MULTIPLE,
/** Create a single 2D swapchain with a viewport per view.
* Stack them horizontally.
*/
SWAPCHAIN_SINGLE,
} SwapchainMode;
/// Set the swapchain management technique to use.
void setSwapchainMode(SwapchainMode mode)
{
_swapchainMode = mode;
}
/// Get the swapchain management technique to use.
SwapchainMode getSwapchainMode() const
{
return _swapchainMode;
}
/// Get mirror settings.
MirrorSettings &getMirrorSettings()
{
return _mirrorSettings;
}
/// Get mirror settings.
const MirrorSettings &getMirrorSettings() const
{
return _mirrorSettings;
}
/**
* Set the number of virtual world units to fit per real world meter.
* This controls the size of the user relative to the virtual world, by
* scaling down the size of the world.
* @param unitsPerMeter The number of units per real world meter.
*/
void setUnitsPerMeter(float unitsPerMeter)
{
_unitsPerMeter = unitsPerMeter;
}
/// Get the number of virtual world units to fit per real world meter.
float getUnitsPerMeter() const
{
return _unitsPerMeter;
}
// Internal APIs
typedef enum {
DIFF_NONE = 0,
DIFF_APP_INFO = (1u << 0),
DIFF_VALIDATION_LAYER = (1u << 1),
DIFF_DEPTH_INFO = (1u << 2),
DIFF_VISIBILITY_MASK = (1u << 3),
DIFF_FORM_FACTOR = (1u << 4),
DIFF_BLEND_MODE = (1u << 5),
DIFF_VR_MODE = (1u << 6),
DIFF_SWAPCHAIN_MODE = (1u << 7),
DIFF_MIRROR = (1u << 8),
DIFF_SCALE = (1u << 9),
} _ChangeMask;
unsigned int _diff(const Settings &other) const;
private:
/*
* Internal data.
*/
// For XrInstance creation
std::string _appName;
uint32_t _appVersion;
bool _validationLayer;
bool _depthInfo;
bool _visibilityMask;
// To get XrSystem
FormFactor _formFactor;
// For choosing environment blend mode
uint32_t _preferredEnvBlendModeMask;
uint32_t _allowedEnvBlendModeMask;
// VR/swapchain modes to use
VRMode _vrMode;
SwapchainMode _swapchainMode;
// Mirror settings
MirrorSettings _mirrorSettings;
// How big the world
float _unitsPerMeter;
};
}
#endif

75
3rdparty/osgXR/include/osgXR/Subaction vendored Normal file
View file

@ -0,0 +1,75 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_Subaction
#define OSGXR_Subaction 1
#include <osgXR/Export>
#include <osg/Referenced>
#include <osg/ref_ptr>
#include <memory>
#include <string>
namespace osgXR {
class InteractionProfile;
class Manager;
/**
* Represents an OpenXR subaction path (a.k.a top level user path).
* This represents an OpenXR subaction path, also referred to in the OpenXR spec
* as a top level user path. These are the physical groupings of inputs, for
* example "/user/head" or "/user/hand/left". Actions and action sets can be
* filtered by subactions, so that the same action (e.g. "shoot") can be read
* separately for different hands.
*/
class OSGXR_EXPORT Subaction : public osg::Referenced
{
public:
/**
* Construct a subaction for a path.
* @param manager The VR manager object to add the action set to.
* @param path The subaction path, e.g. "/user/hand/left".
*/
Subaction(Manager *manager,
const std::string &path);
/// Destructor
virtual ~Subaction();
// Accessors
/// Get the subaction's path.
const std::string &getPath() const;
/// Find the interaction profile bound to the subaction.
InteractionProfile *getCurrentProfile();
class Private;
protected:
// Change handlers
/**
* Notification of change of interaction profile for subaction.
* This is called when the subaction's current interaction profile is
* changed. Derived classes can implement this to their own ends.
* @param newProfile The interaction profile object that is now
* current for the subaction indicated by @p
* subaction. May be nullptr.
*/
virtual void onProfileChanged(InteractionProfile *newProfile);
private:
std::shared_ptr<Private> _private;
};
}
#endif

79
3rdparty/osgXR/include/osgXR/View vendored Normal file
View file

@ -0,0 +1,79 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_View
#define OSGXR_View 1
#include <osgXR/Export>
#include <osg/Camera>
#include <osg/ref_ptr>
#include <osgViewer/GraphicsWindow>
#include <osgViewer/View>
namespace osgXR {
/**
* Representation of a view from an osgXR app point of view.
* This represents a render view that osgXR expects the application to set up.
* This may not directly correspond to OpenXR views, for example if using stereo
* SceneView mode there will be a single view set up for stereo rendering.
*/
class OSGXR_EXPORT View : public osg::Referenced
{
public:
/*
* Application -> osgXR notifications.
*/
/**
* Notify osgXR that a new slave camera has been added to the view.
* This tells osgXR that a new slave camera has been added to the view
* which it should hook into so that it renders to the appropriate
* texture and submits for XR display.
*/
virtual void addSlave(osg::Camera *slaveCamera) = 0;
/**
* Notify osgXR that a slave camera is being removed from the view.
* This tells osgXR when a slave camera previously notified with
* addSlave() is being removed.
*/
virtual void removeSlave(osg::Camera *slaveCamera) = 0;
/*
* Accessors.
*/
/// Get the OSG GraphicsWindow associated with this osgXR view.
inline const osgViewer::GraphicsWindow *getWindow() const
{
return _window;
}
/// Get the OSG View associated with this osgXR view.
inline const osgViewer::View *getView() const
{
return _osgView;
}
protected:
/*
* Internal
*/
View(osgViewer::GraphicsWindow *window, osgViewer::View *osgView);
virtual ~View();
osg::ref_ptr<osgViewer::GraphicsWindow> _window;
osg::ref_ptr<osgViewer::View> _osgView;
};
}
#endif

22
3rdparty/osgXR/include/osgXR/osgXR vendored Normal file
View file

@ -0,0 +1,22 @@
// -*-c++-*-
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#ifndef OSGXR_osgXR
#define OSGXR_osgXR 1
#include <osgXR/Export>
#include <osgViewer/Viewer>
#include <string>
namespace osgXR {
void OSGXR_EXPORT setupViewerDefaults(osgViewer::Viewer *viewer,
const std::string &appName,
uint32_t appVersion);
}
#endif

12
3rdparty/osgXR/osgXR.pc.in vendored Normal file
View file

@ -0,0 +1,12 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
Version: @PROJECT_VERSION@
Requires: openscenegraph OpenXR
Libs: -L${libdir} -lmylib
Cflags: -I${includedir}

507
3rdparty/osgXR/src/Action.cpp vendored Normal file
View file

@ -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());
}

84
3rdparty/osgXR/src/Action.h vendored Normal file
View file

@ -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

242
3rdparty/osgXR/src/ActionSet.cpp vendored Normal file
View file

@ -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();
}

93
3rdparty/osgXR/src/ActionSet.h vendored Normal file
View file

@ -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

132
3rdparty/osgXR/src/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,132 @@
# Dependencies
find_package(OpenGL REQUIRED)
find_package(OpenSceneGraph REQUIRED COMPONENTS osgViewer osgUtil)
find_package(OpenXR REQUIRED)
# Public header files
set(osgXR_HEADERS
include/osgXR/Action
include/osgXR/ActionSet
include/osgXR/Export
include/osgXR/InteractionProfile
include/osgXR/Manager
include/osgXR/Mirror
include/osgXR/MirrorSettings
include/osgXR/OpenXRDisplay
include/osgXR/Settings
include/osgXR/Subaction
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/Session.cpp
OpenXR/Space.cpp
OpenXR/Swapchain.cpp
OpenXR/SwapchainGroup.cpp
OpenXR/System.cpp
XRFramebuffer.cpp
XRState.cpp
XRRealizeOperation.cpp
Action.cpp
ActionSet.cpp
FrameStore.cpp
InteractionProfile.cpp
Manager.cpp
Mirror.cpp
MirrorSettings.cpp
OpenXRDisplay.cpp
Settings.cpp
Subaction.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)
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
)

10
3rdparty/osgXR/src/Config.in vendored Normal file
View file

@ -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
3rdparty/osgXR/src/FrameStampedVector.h vendored Normal file
View file

@ -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

87
3rdparty/osgXR/src/FrameStore.cpp vendored Normal file
View file

@ -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;
}

63
3rdparty/osgXR/src/FrameStore.h vendored Normal file
View file

@ -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

View file

@ -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
3rdparty/osgXR/src/InteractionProfile.h vendored Normal file
View file

@ -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

191
3rdparty/osgXR/src/Manager.cpp vendored Normal file
View file

@ -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();
}
}

139
3rdparty/osgXR/src/Mirror.cpp vendored Normal file
View file

@ -0,0 +1,139 @@
// 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;
state->setMode(GL_LIGHTING, forceOff);
state->setMode(GL_DEPTH_TEST, forceOff);
_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
3rdparty/osgXR/src/MirrorSettings.cpp vendored Normal file
View file

@ -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)
{
}

188
3rdparty/osgXR/src/OpenXR/Action.cpp vendored Normal file
View file

@ -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");
}

371
3rdparty/osgXR/src/OpenXR/Action.h vendored Normal file
View file

@ -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
3rdparty/osgXR/src/OpenXR/ActionSet.cpp vendored Normal file
View file

@ -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
3rdparty/osgXR/src/OpenXR/ActionSet.h vendored Normal file
View file

@ -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

View file

@ -0,0 +1,71 @@
// 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;
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);
}

92
3rdparty/osgXR/src/OpenXR/Compositor.h vendored Normal file
View file

@ -0,0 +1,92 @@
// 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;
};
} // osgXR::OpenXR
} // osgXR
#endif

80
3rdparty/osgXR/src/OpenXR/DepthInfo.h vendored Normal file
View file

@ -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

View file

@ -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)
{
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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();
}

View file

@ -0,0 +1,29 @@
// 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>
#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

View file

@ -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();
}

View file

@ -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

398
3rdparty/osgXR/src/OpenXR/Instance.cpp vendored Normal file
View file

@ -0,0 +1,398 @@
// 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;
if (res == XR_ERROR_INSTANCE_LOST)
return INIT_LATER;
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;
}
// 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);
}
}

179
3rdparty/osgXR/src/OpenXR/Instance.h vendored Normal file
View file

@ -0,0 +1,179 @@
// 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 <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;
}
// 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;
// Systems
mutable std::vector<System *> _systems;
// Sessions
std::map<XrSession, Session *> _sessions;
};
} // osgXR::OpenXR
} // osgXR
#endif

View file

@ -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");
}

View file

@ -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

41
3rdparty/osgXR/src/OpenXR/Path.cpp vendored Normal file
View file

@ -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();
}

80
3rdparty/osgXR/src/OpenXR/Path.h vendored Normal file
View file

@ -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

519
3rdparty/osgXR/src/OpenXR/Session.cpp vendored Normal file
View file

@ -0,0 +1,519 @@
// 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 currentSet = checkCurrent();
// As of 2021-12-16 Monado expects the GL context to be current
// See https://gitlab.freedesktop.org/monado/monado/-/issues/145
if (!currentSet)
makeCurrent();
if (check(xrCreateSession(getXrInstance(), &createInfo, &_session),
"Failed to create OpenXR session"))
{
_instance->registerSession(this);
}
if (!currentSet)
releaseContext();
}
Session::~Session()
{
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");
}
}
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()
{
assert(valid());
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()
{
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 currentSet = _session->checkCurrent();
bool ret = check(xrEndFrame(_session->getXrSession(), &frameEndInfo),
"Failed to end OpenXR frame");
// TODO: should not be necessary, but is for SteamVR 1.16.4 (but not 1.15.x)
if (currentSet)
_session->makeCurrent();
return ret;
}

376
3rdparty/osgXR/src/OpenXR/Session.h vendored Normal file
View file

@ -0,0 +1,376 @@
// 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();
// 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
bool checkCurrent() const;
void makeCurrent() const;
void releaseContext() const;
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 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

94
3rdparty/osgXR/src/OpenXR/Space.cpp vendored Normal file
View file

@ -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() && 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;
}

126
3rdparty/osgXR/src/OpenXR/Space.h vendored Normal file
View file

@ -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

170
3rdparty/osgXR/src/OpenXR/Swapchain.cpp vendored Normal file
View file

@ -0,0 +1,170 @@
// 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 currentSet = _session->checkCurrent();
// As of 2021-12-16 Monado expects the GL context to be current
// See https://gitlab.freedesktop.org/monado/monado/-/issues/145
if (!currentSet)
_session->makeCurrent();
// GL context must not be bound in another thread
check(xrCreateSwapchain(getXrSession(), &createInfo, &_swapchain),
"Failed to create OpenXR swapchain");
if (currentSet)
// SteamVR 1.16.4 (but not 1.15.x) change context then clear it
_session->makeCurrent();
else
// SteamVR linux_v1.14 changes context without changing back
// Monado doesn't change it at all (see above)
_session->releaseContext();
}
Swapchain::~Swapchain()
{
if (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 currentSet = _session->checkCurrent();
// GL context must not be bound in another thread
if (check(xrAcquireSwapchainImage(_swapchain, nullptr, &imageIndex),
"Failed to acquire swapchain image"))
{
// TODO: should not be necessary, but is for SteamVR 1.16.4 (but not 1.15.x)
if (currentSet)
_session->makeCurrent();
return imageIndex;
}
// TODO: should not be necessary, but is for SteamVR 1.16.4 (but not 1.15.x)
if (currentSet)
_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 currentSet = _session->checkCurrent();
// GL context must not be bound in another thread
bool ret = check(xrWaitSwapchainImage(_swapchain, &waitInfo),
"Failed to wait for swapchain image");
// TODO: should not be necessary, but is for SteamVR 1.16.4 (but not 1.15.x)
if (currentSet)
_session->makeCurrent();
return ret;
}
void Swapchain::releaseImage() const
{
// Release the swapchain image
bool currentSet = _session->checkCurrent();
// GL context must not be bound in another thread
check(xrReleaseSwapchainImage(_swapchain, nullptr),
"Failed to release OpenXR swapchain image");
// TODO: should not be necessary, but is for SteamVR 1.16.4 (but not 1.15.x)
if (currentSet)
_session->makeCurrent();
}

119
3rdparty/osgXR/src/OpenXR/Swapchain.h vendored Normal file
View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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

122
3rdparty/osgXR/src/OpenXR/System.cpp vendored Normal file
View file

@ -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;
}

221
3rdparty/osgXR/src/OpenXR/System.h vendored Normal file
View file

@ -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

42
3rdparty/osgXR/src/OpenXRDisplay.cpp vendored Normal file
View file

@ -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));
}

63
3rdparty/osgXR/src/Settings.cpp vendored Normal file
View file

@ -0,0 +1,63 @@
// 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),
_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 (_mirrorSettings != other._mirrorSettings)
ret |= DIFF_MIRROR;
if (_unitsPerMeter != other._unitsPerMeter)
ret |= DIFF_SCALE;
return ret;
}

109
3rdparty/osgXR/src/Subaction.cpp vendored Normal file
View file

@ -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
}

75
3rdparty/osgXR/src/Subaction.h vendored Normal file
View file

@ -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

12
3rdparty/osgXR/src/Version.h.in vendored Normal file
View file

@ -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

16
3rdparty/osgXR/src/View.cpp vendored Normal file
View file

@ -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()
{
}

151
3rdparty/osgXR/src/XRFramebuffer.cpp vendored Normal file
View file

@ -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;
}
}

52
3rdparty/osgXR/src/XRFramebuffer.h vendored Normal file
View file

@ -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

View file

@ -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
3rdparty/osgXR/src/XRRealizeOperation.h vendored Normal file
View file

@ -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

1589
3rdparty/osgXR/src/XRState.cpp vendored Normal file

File diff suppressed because it is too large Load diff

590
3rdparty/osgXR/src/XRState.h vendored Normal file
View file

@ -0,0 +1,590 @@
// 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/InteractionProfile>
#include <osgXR/Settings>
#include <osgXR/Subaction>
#include <osgXR/View>
#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 chosenSwapchainFormat,
int64_t chosenDepthSwapchainFormat);
// GL context must be current (for XRFramebuffer)
virtual ~XRSwapchain();
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;
/// 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
{
return _probing ? VRSTATE_SYSTEM : 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();
/// 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 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);
}
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);
// Set up a swapchain for each view
bool setupMultipleSwapchains(int64_t format, int64_t depthFormat = 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;
/// 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

195
3rdparty/osgXR/src/XRStateCallbacks.h vendored Normal file
View file

@ -0,0 +1,195 @@
// 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);
}
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

94
3rdparty/osgXR/src/osgXR.cpp vendored Normal file
View file

@ -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::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;
}
}

79
3rdparty/osgXR/src/projection.cpp vendored Normal file
View file

@ -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;
}
}

20
3rdparty/osgXR/src/projection.h vendored Normal file
View file

@ -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