Compare commits

..

54 commits

Author SHA1 Message Date
Simon
79befe4d38
Merge pull request #92 from lvonasek/feature_remote_keyboard
Support for BT/OTG keyboards
2022-12-04 14:39:17 +00:00
Lubos
91fa5bcf3b Handle only suppoted characters 2022-12-03 11:47:22 +01:00
Lubos
8bb0124fe6 Support external keyboards 2022-12-02 09:58:21 +01:00
Simon
6d9951dd95
Merge pull request #91 from petr666/master
Implement virtual gun stock (OpenXR)
2022-10-29 19:28:06 +01:00
Petr Bartos
b904ee0d4f Fix virtual gun stock height adjustment 2022-10-20 08:46:45 +02:00
Petr Bartos
61faf0a731 Implement virtual gun stock 2022-10-19 22:20:39 +02:00
Simon
a235a57205 Update version numbers 2022-09-30 20:10:19 +01:00
Simon
325deba431 Revert "Merge pull request #87 from foxtrotdev/feature/app-improvements-1"
This reverts commit c65bff06ad.
2022-09-30 20:10:19 +01:00
Simon
b2bd1be433
Merge pull request #87 from foxtrotdev/feature/app-improvements-1
[Android] Correct versioning, increase gradle version, new launcher icon, configs clean up
2022-09-30 18:40:27 +01:00
Simon
e1cdb7a9c2
Merge pull request #89 from petr666/master
Configurable supersampling, added 120hz option, small cosmetic change (master/OpenXr)
2022-09-30 18:40:13 +01:00
Simon
493ee6e96d
Merge branch 'master' into master 2022-09-30 18:40:05 +01:00
Simon
9634ff74cd
Merge pull request #86 from lvonasek/feature_openxr_tweaks
Make OpenXR build behave the same like VrAPI build
2022-09-30 18:37:58 +01:00
Petr Bartos
706481fe00 Add 1.3 option for supersampling 2022-09-29 22:06:20 +02:00
Petr Bartos
21fc40dc44 Do not draw weapon model, crosshair and hud in menu screen 2022-09-29 17:41:20 +02:00
Petr Bartos
96b1071b50 Add super sampling configuration and 120hz refresh rate 2022-09-29 17:38:20 +02:00
Lubos
807a8d2820 OpenXR - Make the initializations more universal 2022-09-02 12:28:14 +02:00
Michał Mokrzycki
c7811f31d1 feature: return to compileSdkVersion 29 2022-09-01 09:20:21 +02:00
Michał Mokrzycki
65dcb19541 feat: correct versioning (moved to gradle file)
feat: increased gradle version to 7.2.1
feat: add launcher icons (from Wikipedia - https://de.wikipedia.org/wiki/Datei:Quake_III_Arena_Logo.svg - author: unknown)
2022-08-09 17:47:12 +02:00
Lubos
b43a9a67c7 Make OpenXR build behave the same like VrAPI build 2022-07-05 16:28:08 +02:00
Simon
b11fc0fb29 QoL tweaks
- Make the frag / flag / time limit values modifiable lists of values, rather than text entry
- Couple of changes to the credits screen
2022-06-12 11:42:41 +01:00
Simon
023d0f27a8 Master Server address change
- Change cvar name (vr_master) and value to use mp.quakevr.com:27950
- Added Omarlego's background pak
2022-05-23 21:12:48 +01:00
Simon
bd0592dcc7 Update ui_credits.c 2022-05-22 10:21:33 +01:00
Simon
152e48b3c7 Update ui_credits.c 2022-05-22 10:16:21 +01:00
Simon
e201285822 Fixed incorrect credits 2022-05-21 20:59:06 +01:00
Simon
1f3a31bbc8 Allow a Quest running the demo pak to connect to another quest server 2022-05-21 09:37:52 +01:00
Simon
096daff966 Ensure roll angles are passed to the server if enabled 2022-05-19 22:34:32 +01:00
Simon
f5c507fe8a
Merge pull request #84 from petr666/master
Delete obsolete glsl folder on startup
2022-05-19 19:30:40 +01:00
Simon
b45d7cd653 Grey out non-demo servers if playing with demo pak0
- don't allow players to even start a MP game on a non-demo server
- fixed the website url
- updated the message about the demo in the main menu
2022-05-19 19:11:00 +01:00
Petr Bartos
d322b861f2 Do not create missionpack folder if not installed 2022-05-18 22:48:05 +02:00
Petr Bartos
33e4bc37ad Delete obsolete glsl folder on startup 2022-05-16 21:37:43 +02:00
Simon
c01c8b3484 Change to multiplayer menu message
also disabled the "specify" button as it is useless in this port
2022-05-15 18:06:51 +01:00
Simon
d9db6a2076
Merge pull request #83 from petr666/feature/control-tweaks
Remap face buttons along with switching thumbsticks
2022-05-15 16:09:33 +01:00
Simon
b659a9e09e Support for ForeceTube/ProTube Stock
Also added f2hunter to the credits
2022-05-15 10:34:43 +01:00
Petr Bartos
8cf8f3ae1b Remap face buttons along with switching thumbsticks 2022-05-14 19:30:50 +02:00
Simon
8cf2ffc445
Merge pull request #82 from petr666/master
Generate random suffix for default player name
2022-05-14 12:20:19 +01:00
Simon
d90f86e035
Merge pull request #81 from lvonasek/hotfix_openxr
OpenXR jitter hotfix
2022-05-14 12:19:37 +01:00
Petr Bartos
e903dc373b Update also already stored default player name with random suffix 2022-05-14 12:41:24 +02:00
Petr Bartos
d4785e8a9e Generate random suffix for default player name 2022-05-14 12:06:55 +02:00
Lubos
0a3d613787 Revert "OpenXR recenter after the first frame"
This reverts commit 5068ce7199.
2022-05-13 18:31:16 +02:00
Lubos
f2dbc90823 OpenXR controllers coordinates relative to head space 2022-05-11 16:51:07 +02:00
Lubos
5068ce7199 OpenXR recenter after the first frame 2022-05-11 16:11:54 +02:00
Lubos
5ecbbac97e OpenXR restore haptics calling 2022-05-10 22:30:10 +02:00
Lubos
d354212c5d OpenXR change the way of getting the head position 2022-05-10 22:26:55 +02:00
Lubos
859cecb424 OpenXR getting view matrix simplified 2022-05-10 20:35:53 +02:00
Lubos
244b8344ff OpenXR do not call stop haptics every frame 2022-05-10 19:34:18 +02:00
Lubos
437e3829aa OpenXR ignore controller velocity 2022-05-10 19:23:51 +02:00
Lubos
314e8b0288 OpenXR attach actions to session just once 2022-05-10 19:21:13 +02:00
Simon
709c0abdcf
Merge pull request #80 from lvonasek/hotfix_app_flow
Hotfix app flow
2022-05-08 19:24:50 +01:00
Lubos
9d09e3f9d8 Start the game on clean install fix 2022-05-08 08:50:25 +02:00
Lubos
a56b9ad288 OpenXR set yaw to zero on a fresh start 2022-05-08 08:46:59 +02:00
Simon
7e092d57a4 Set RC1 version number 2022-05-07 19:24:48 +01:00
Simon
4181466815
Merge pull request #79 from lvonasek/hotfix_railgun_zoom
OpenXR calculate FOV as average of both eyes
2022-05-07 19:03:12 +01:00
Lubos
65ff5bbd80 OpenXR calculate FOV as average of both eyes 2022-05-07 19:31:37 +02:00
Simon
52799aa99d Squashed commit of the following:
commit 0adeb4ba3b
Merge: 7feea565 d8653ad7
Author: Simon <simonbrown77@googlemail.com>
Date:   Sat May 7 14:32:21 2022 +0100

    Merge branch 'OpenXR' of https://github.com/DrBeef/ioq3quest into OpenXR

commit 7feea56514
Author: Simon <simonbrown77@googlemail.com>
Date:   Sat May 7 14:32:14 2022 +0100

    Update Version Number

commit d8653ad70e
Merge: 79983b2b 7e6aa6da
Author: Simon <simonbrown77@googlemail.com>
Date:   Sat May 7 14:31:45 2022 +0100

    Merge pull request #76 from lvonasek/OpenXR

    OpenXR delayed controller pose fixed

commit 79983b2bc5
Merge: 325641f9 7f89d6d1
Author: Simon <simonbrown77@googlemail.com>
Date:   Sat May 7 14:31:31 2022 +0100

    Merge pull request #77 from petr666/OpenXR

    Update gamma values to better suit OpenXR build

commit 7f89d6d19b
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat May 7 13:56:03 2022 +0200

    Update gamma values to better suit OpenXR build

commit 7e6aa6dab8
Author: Lubos <tridosm@gmail.com>
Date:   Sat May 7 11:32:11 2022 +0200

    OpenXR sync actions before updating controllers

commit 027a7e96c3
Author: Lubos <tridosm@gmail.com>
Date:   Fri May 6 10:34:50 2022 +0200

    OpenXR implement controller velocity again

commit af80d5c284
Author: Lubos <tridosm@gmail.com>
Date:   Fri May 6 10:18:51 2022 +0200

    Compile fix

commit ab1e853554
Author: Lubos <tridosm@gmail.com>
Date:   Fri May 6 10:14:08 2022 +0200

    OpenXR input cleanup

commit be158b4ad9
Author: Lubos <tridosm@gmail.com>
Date:   Fri May 6 09:39:49 2022 +0200

    OpenXR sync input actions from renderer

commit ed96160ffc
Author: Lubos <tridosm@gmail.com>
Date:   Fri May 6 09:25:59 2022 +0200

    OpenXR do not use pose velocity to track controllers

commit 42bf984ef8
Author: Lubos <tridosm@gmail.com>
Date:   Thu May 5 22:39:10 2022 +0200

    OpenXR apply controller pose from renderer

commit 92941b3547
Author: Lubos <tridosm@gmail.com>
Date:   Thu May 5 22:26:33 2022 +0200

    OpenXR delayed controller pose fixed

commit 325641f90e
Merge: de198fba 3ee3f590
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu May 5 16:30:07 2022 +0100

    Merge pull request #75 from lvonasek/OpenXR

    OpenXR menuyaw and recenter support added

commit 3ee3f59058
Author: Lubos <tridosm@gmail.com>
Date:   Thu May 5 12:22:36 2022 +0200

    OpenXR do not recenter on renderer reload

commit 7072b0b89e
Author: Lubos <tridosm@gmail.com>
Date:   Wed May 4 21:05:45 2022 +0200

    OpenXR recenter sets yaw always to 0

commit 8e5f6414a6
Author: Lubos <tridosm@gmail.com>
Date:   Wed May 4 16:19:48 2022 +0200

    OpenXR recenter integrated

commit 08a4fc7fbf
Author: Lubos <tridosm@gmail.com>
Date:   Tue May 3 18:37:28 2022 +0200

    OpenXR use the same coordinate system in all layers

commit a1de0e1ffe
Author: Lubos <tridosm@gmail.com>
Date:   Mon May 2 22:17:53 2022 +0200

    OpenXR menuYaw support added

commit de198fba0d
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon May 2 10:01:42 2022 +0100

    Update version numbers and add openxr identifier

commit b83f99fe5b
Merge: c623bf13 9c14728b
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon May 2 09:43:47 2022 +0100

    Merge pull request #74 from lvonasek/OpenXR

    OpenXR left controller mapping fixed

commit 9c14728bc9
Merge: 11a849b1 0695c7ac
Author: Lubos <tridosm@gmail.com>
Date:   Sun May 1 19:41:45 2022 +0200

    Merge branch 'master' into OpenXR

commit 11a849b189
Author: Lubos <tridosm@gmail.com>
Date:   Sun May 1 19:09:12 2022 +0200

    OpenXR controller mapping fixed

commit 712584eae8
Merge: df4bc0c0 c623bf13
Author: Luboš Vonásek <tridosm@gmail.com>
Date:   Sun May 1 19:04:45 2022 +0200

    Merge branch 'DrBeef:OpenXR' into OpenXR

commit c623bf1399
Merge: cb152467 e42a2f4e
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun May 1 14:10:23 2022 +0100

    Merge pull request #72 from lvonasek/OpenXR

    OpenXR jitter fixed

commit e42a2f4e0d
Author: Lubos <tridosm@gmail.com>
Date:   Sun May 1 14:34:38 2022 +0200

    Compile fix

commit acbff1e736
Author: Lubos <tridosm@gmail.com>
Date:   Sun May 1 13:59:41 2022 +0200

    OpenXR camera view moved into renderer

commit cb152467ba
Merge: decc2d02 26071937
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun May 1 10:20:40 2022 +0100

    Merge pull request #69 from lvonasek/feature_openxr

    OpenXR integration

commit 26071937c8
Author: Lubos <tridosm@gmail.com>
Date:   Sun May 1 11:17:50 2022 +0200

    OpenXR frameskip integrated

commit f735535dce
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 30 22:57:48 2022 +0200

    OpenXR attempt to fix flickering

commit 0eb1d43b4a
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 30 21:28:26 2022 +0200

    2D aspect ratio fixed

commit 9d07c27604
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 30 21:23:53 2022 +0200

    Hud model rendering fixed

commit 30c9b4eaa0
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 30 17:03:54 2022 +0200

    Release build fixed

commit 405ca571ad
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 30 14:10:27 2022 +0200

    OpenXR foveation removed

commit 61b3192bce
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 30 14:02:21 2022 +0200

    OpenXR projection matrix calculation from example code

commit ecd50dbcb1
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 29 21:21:10 2022 +0200

    OpenXR low framerate fixed

commit f5a8429fc1
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 29 19:21:29 2022 +0200

    Solution for black rectangles in the scene

commit 9ae93a5fe9
Merge: 10548445 decc2d02
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 29 14:58:35 2022 +0200

    Merge branch 'master' into feature_openxr

commit 1054844524
Merge: 65e2031e 3cdf1858
Author: Lubos <tridosm@gmail.com>
Date:   Wed Apr 27 21:52:55 2022 +0200

    Merge branch 'master' into feature_openxr

commit 65e2031e95
Author: Lubos <tridosm@gmail.com>
Date:   Tue Apr 26 22:02:04 2022 +0200

    OpenXR integration fixes

commit c321b97894
Author: Lubos <tridosm@gmail.com>
Date:   Mon Apr 25 16:48:09 2022 +0200

    OpenXR multiview integrated

commit 5947168966
Author: Lubos <tridosm@gmail.com>
Date:   Sun Apr 24 20:25:19 2022 +0200

    OpenXR minor fixes

commit 249847930b
Author: Lubos <tridosm@gmail.com>
Date:   Sun Apr 24 18:06:02 2022 +0200

    OpenXR use just one framebuffer

commit b11122e368
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 23 21:06:35 2022 +0200

    OpenXR touch controllers completely implemented

commit 0bf485664c
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 23 19:44:18 2022 +0200

    OpenXR haptics and refreshrate fixed

commit 4716387e52
Author: Lubos <tridosm@gmail.com>
Date:   Sat Apr 23 15:54:07 2022 +0200

    OpenXR input except buttons implemented

commit 7bd1fe31c2
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 22 21:18:49 2022 +0200

    OpenXR input in progress

commit 3c26e08f01
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 22 18:41:26 2022 +0200

    Old code removed

commit 4d2644cae2
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 22 18:32:42 2022 +0200

    OpenXR fov fixed

commit 501869a051
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 22 17:56:14 2022 +0200

    OpenXR motion tracking in game mode added

commit b152d21ddc
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 22 16:14:36 2022 +0200

    OpenXR cylinder projection added

commit 3e9c80679e
Merge: 0de4a0d1 39f669dd
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 22 14:13:18 2022 +0200

    Merge branch 'master' into feature_openxr

commit 0de4a0d196
Author: Lubos <tridosm@gmail.com>
Date:   Fri Apr 22 13:45:22 2022 +0200

    OpenXR tracking added init, first working rendering

commit 8431b1aa83
Author: Lubos <tridosm@gmail.com>
Date:   Thu Apr 21 21:15:02 2022 +0200

    OpenXR add missing foveation

commit 4ff90d8ba0
Author: Lubos <tridosm@gmail.com>
Date:   Thu Apr 21 21:00:02 2022 +0200

    OpenXR renderer cleanup

commit d736af15bd
Merge: 7b9d51ec fe180129
Author: Lubos <tridosm@gmail.com>
Date:   Thu Mar 31 17:47:45 2022 +0200

    Merge branch 'master' into feature_openxr

commit 7b9d51ec30
Author: Lubos <tridosm@gmail.com>
Date:   Thu Mar 31 17:47:36 2022 +0200

    OpenXR renderer in progress

commit e7a2229edc
Merge: abeaf046 bcf9287a
Author: Lubos <tridosm@gmail.com>
Date:   Wed Mar 30 09:44:15 2022 +0200

    Merge branch 'master' into feature_openxr

commit abeaf04607
Author: Lubos <tridosm@gmail.com>
Date:   Tue Mar 29 19:57:47 2022 +0200

    OpenXR initialization fixed

commit f2fefbf708
Author: Lubos <tridosm@gmail.com>
Date:   Tue Mar 29 17:13:48 2022 +0200

    OpenXR initialization added

commit 96bf69cb15
Author: Lubos <tridosm@gmail.com>
Date:   Tue Mar 29 12:56:37 2022 +0200

    VR API removed
2022-05-07 14:34:37 +01:00
22 changed files with 1909 additions and 515 deletions

2
.gitignore vendored
View file

@ -1,4 +1,6 @@
build
android/app/src/main/cpp/code/OpenXR-SDK
android/app/src/main/cpp/code/OpenXR
android/app/src/main/cpp/code/VrApi
*.swp
*tags

View file

@ -281,7 +281,8 @@ LOKISETUPDIR=misc/setup
NSISDIR=misc/nsis
SDLHDIR=$(MOUNT_DIR)/SDL2
LIBSDIR=$(MOUNT_DIR)/libs
VRAPIDIR=$(MOUNT_DIR)/VrApi
OPENXRDIR=$(MOUNT_DIR)/OpenXR
OPENXRSDKDIR=$(MOUNT_DIR)/OpenXR-SDK
bin_path=$(shell which $(1) 2> /dev/null)
@ -468,11 +469,11 @@ ifeq ($(PLATFORM),android)
RENDERER_LIBS += $(LIBSDIR)/android/arm64-v8a/libSDL2.so
CLIENT_EXTRA_FILES += $(LIBSDIR)/android/arm64-v8a/libSDL2.so
# VrApi
BASE_CFLAGS += -I$(VRAPIDIR)/Include
CLIENT_LIBS += $(VRAPIDIR)/Libs/Android/arm64-v8a/Release/libvrapi.so
RENDERER_LIBS += $(VRAPIDIR)/Libs/Android/arm64-v8a/Release/libvrapi.so
CLIENT_EXTRA_FILES += $(VRAPIDIR)/Libs/Android/arm64-v8a/Release/libvrapi.so
# OpenXR
BASE_CFLAGS += -I$(OPENXRDIR)/Include -I$(OPENXRSDKDIR)/include
CLIENT_LIBS += $(OPENXRDIR)/Libs/Android/arm64-v8a/Release/libopenxr_loader.so
RENDERER_LIBS += $(OPENXRDIR)/Libs/Android/arm64-v8a/Release/libopenxr_loader.so
CLIENT_EXTRA_FILES += $(OPENXRDIR)/Libs/Android/arm64-v8a/Release/libopenxr_loader.so
else # ifeq Android
#############################################################################
@ -1819,6 +1820,7 @@ Q3OBJ = \
$(B)/client/vr_base.o \
$(B)/client/vr_input.o \
$(B)/client/vr_renderer.o \
$(B)/client/vr_types.o \
\
$(B)/client/con_log.o \
$(B)/client/sys_autoupdater.o \

View file

@ -5,7 +5,9 @@
### Prerequisites
1. Install your copy of Quake III Arena from Steam.
2. Android Studio with NDK version 21.1.6352462.
3. Copy the Oculus VR SDK from https://developer.oculus.com/downloads/package/oculus-mobile-sdk/ and extract the VrApi folder to ./android/app/src/main/cpp/code/VrApi/
3. Download the Oculus OpenXR SDK from https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/
4. Extract the OpenXR folder to ./android/app/src/main/cpp/code/OpenXR/
5. Extract the 3rdParty/khronos/openxr/OpenXR-SDK folder to ./android/app/src/main/cpp/code/OpenXR-SDK/
### Building and running the build
The scripts assume that you installed everything in the default locations. In case you want to deviate from that, the paths are in ./android/run.(sh|bat) and in Makefile.local.

View file

@ -15,7 +15,7 @@ endif()
add_custom_target(copy_libs
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/libioquake3_opengl2.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libioquake3_opengl2.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/libSDL2.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libSDL2.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/libvrapi.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libvrapi.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/libopenxr_loader.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libopenxr_loader.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/baseq3/cgameaarch64.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libcgameaarch64_baseq3.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/baseq3/qagameaarch64.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libqagameaarch64_baseq3.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/baseq3/uiaarch64.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libuiaarch64_baseq3.so
@ -27,7 +27,8 @@ add_custom_target(copy_libs
add_dependencies(main copy_libs)
target_include_directories(main PRIVATE
${CMAKE_SOURCE_DIR}/code/VrApi/Include
${CMAKE_SOURCE_DIR}/code/OpenXR-SDK/include
${CMAKE_SOURCE_DIR}/code/OpenXR/Include
${CMAKE_SOURCE_DIR}/code/SDL2/include
${CMAKE_SOURCE_DIR}/code)
@ -40,5 +41,5 @@ target_link_libraries(main
android
ioquake3_opengl2
SDL2
vrapi
openxr_loader
${log-lib})

View file

@ -790,8 +790,8 @@ void GraphicsOptions_MenuInit( void )
s_graphicsoptions.gamma.generic.y = y;
s_graphicsoptions.gamma.generic.callback = GraphicsOptions_Event;
s_graphicsoptions.gamma.generic.id = ID_GAMMA;
s_graphicsoptions.gamma.minvalue = 0.8f;
s_graphicsoptions.gamma.maxvalue = 1.2f;
s_graphicsoptions.gamma.minvalue = 0.6f;
s_graphicsoptions.gamma.maxvalue = 1.0f;
y += BIGCHAR_HEIGHT+2;
// references "cg_oldRail"

View file

@ -24,13 +24,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "tr_types.h"
#if __ANDROID__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#include <VrApi.h>
#pragma clang diagnostic pop
#endif
#define REF_API_VERSION 8
//
@ -92,8 +85,9 @@ typedef struct {
void (*EndFrame)( int *frontEndMsec, int *backEndMsec );
#if __ANDROID__
void (*SetVRHeadsetParms)( const ovrMatrix4f *projectionMatrix, const ovrMatrix4f *nonVRProjectionMatrix,
int renderBuffer );
void (*SetVRHeadsetParms)( const float projectionMatrix[4][4],
const float nonVRProjectionMatrix[4][4],
int renderBuffer );
#endif
int (*MarkFragments)( int numPoints, const vec3_t *points, const vec3_t projection,

View file

@ -513,11 +513,10 @@ void R_Mat4Transpose( const float in[4][4], float* out ) {
}
}
void RE_SetVRHeadsetParms( const ovrMatrix4f *projectionMatrix, const ovrMatrix4f *nonVRProjectionMatrix,
void RE_SetVRHeadsetParms( const float projectionMatrix[4][4], const float nonVRProjectionMatrix[4][4],
int renderBuffer ) {
R_Mat4Transpose(projectionMatrix->M, tr.vrParms.projection);
R_Mat4Transpose(nonVRProjectionMatrix->M, tr.vrParms.monoVRProjection);
R_Mat4Transpose(projectionMatrix, tr.vrParms.projection);
R_Mat4Transpose(nonVRProjectionMatrix, tr.vrParms.monoVRProjection);
tr.vrParms.renderBuffer = renderBuffer;
tr.vrParms.valid = qtrue;
}

View file

@ -1322,7 +1322,7 @@ void R_Register( void )
r_textureMode = ri.Cvar_Get( "r_textureMode", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
r_swapInterval = ri.Cvar_Get( "r_swapInterval", "0",
CVAR_ARCHIVE | CVAR_LATCH );
r_gamma = ri.Cvar_Get( "r_gamma", "1", CVAR_ARCHIVE );
r_gamma = ri.Cvar_Get( "r_gamma", "0.8", CVAR_ARCHIVE );
r_facePlaneCull = ri.Cvar_Get ("r_facePlaneCull", "1", CVAR_ARCHIVE );
r_railWidth = ri.Cvar_Get( "r_railWidth", "16", CVAR_ARCHIVE );

View file

@ -2535,8 +2535,9 @@ void RE_StretchPic ( float x, float y, float w, float h,
void RE_BeginFrame( stereoFrame_t stereoFrame );
void RE_EndFrame( int *frontEndMsec, int *backEndMsec );
#if __ANDROID__
void RE_SetVRHeadsetParms( const ovrMatrix4f *projectionMatrix, const ovrMatrix4f *nonVRProjectionMatrix,
int renderBuffer );
void RE_SetVRHeadsetParms( const float projectionMatrix[4][4],
const float nonVRProjectionMatrix[4][4],
int renderBuffer );
#endif
void RE_HUDBufferStart( qboolean clear );
void RE_HUDBufferEnd( void );

View file

@ -1,22 +1,26 @@
#include "vr_base.h"
#include "../VrApi/Include/VrApi.h"
#include "../VrApi/Include/VrApi_Helpers.h"
#include "../VrApi/Include/VrApi_Types.h"
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../client/client.h"
#include "vr_base.h"
#include "vr_clientinfo.h"
//#if __ANDROID__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#pragma clang diagnostic pop
#include <EGL/egl.h>
#include <assert.h>
#include <unistd.h>
static engine_t vr_engine;
qboolean vr_initialized = qfalse;
extern vr_clientinfo_t vr;
const char* const requiredExtensionNames[] = {
XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME,
XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME,
XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME,
XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME,
XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME};
const uint32_t numRequiredExtensions =
sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]);
cvar_t *vr_worldscale = NULL;
cvar_t *vr_worldscaleScaler = NULL;
@ -47,18 +51,92 @@ cvar_t *vr_showConsoleMessages = NULL;
engine_t* VR_Init( ovrJava java )
{
ovrInitParms initParams;
ovrResult initResult;
if (vr_initialized)
return &vr_engine;
memset(&vr_engine, 0, sizeof(vr_engine));
ovrApp_Clear(&vr_engine.appState);
initParams = vrapi_DefaultInitParms(&java);
initResult = vrapi_Initialize(&initParams);
assert(initResult == VRAPI_INITIALIZE_SUCCESS);
vr_engine.java = java;
PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
xrGetInstanceProcAddr(
XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR);
if (xrInitializeLoaderKHR != NULL) {
XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid;
memset(&loaderInitializeInfoAndroid, 0, sizeof(loaderInitializeInfoAndroid));
loaderInitializeInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
loaderInitializeInfoAndroid.next = NULL;
loaderInitializeInfoAndroid.applicationVM = java.Vm;
loaderInitializeInfoAndroid.applicationContext = java.ActivityObject;
xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid);
}
return &vr_engine;
// Create the OpenXR instance.
XrApplicationInfo appInfo;
memset(&appInfo, 0, sizeof(appInfo));
strcpy(appInfo.applicationName, "Quake 3 Arena");
appInfo.applicationVersion = 0;
strcpy(appInfo.engineName, "Quake 3 Arena");
appInfo.engineVersion = 0;
appInfo.apiVersion = XR_CURRENT_API_VERSION;
XrInstanceCreateInfo instanceCreateInfo;
memset(&instanceCreateInfo, 0, sizeof(instanceCreateInfo));
instanceCreateInfo.type = XR_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.next = NULL;
instanceCreateInfo.createFlags = 0;
instanceCreateInfo.applicationInfo = appInfo;
instanceCreateInfo.enabledApiLayerCount = 0;
instanceCreateInfo.enabledApiLayerNames = NULL;
instanceCreateInfo.enabledExtensionCount = numRequiredExtensions;
instanceCreateInfo.enabledExtensionNames = requiredExtensionNames;
XrResult initResult;
OXR(initResult = xrCreateInstance(&instanceCreateInfo, &vr_engine.appState.Instance));
if (initResult != XR_SUCCESS) {
ALOGE("Failed to create XR instance: %d.", initResult);
exit(1);
}
XrInstanceProperties instanceInfo;
instanceInfo.type = XR_TYPE_INSTANCE_PROPERTIES;
instanceInfo.next = NULL;
OXR(xrGetInstanceProperties(vr_engine.appState.Instance, &instanceInfo));
ALOGV(
"Runtime %s: Version : %u.%u.%u",
instanceInfo.runtimeName,
XR_VERSION_MAJOR(instanceInfo.runtimeVersion),
XR_VERSION_MINOR(instanceInfo.runtimeVersion),
XR_VERSION_PATCH(instanceInfo.runtimeVersion));
XrSystemGetInfo systemGetInfo;
memset(&systemGetInfo, 0, sizeof(systemGetInfo));
systemGetInfo.type = XR_TYPE_SYSTEM_GET_INFO;
systemGetInfo.next = NULL;
systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
XrSystemId systemId;
OXR(initResult = xrGetSystem(vr_engine.appState.Instance, &systemGetInfo, &systemId));
if (initResult != XR_SUCCESS) {
ALOGE("Failed to get system.");
exit(1);
}
// Get the graphics requirements.
PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL;
OXR(xrGetInstanceProcAddr(
vr_engine.appState.Instance,
"xrGetOpenGLESGraphicsRequirementsKHR",
(PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR)));
XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = {};
graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR;
OXR(pfnGetOpenGLESGraphicsRequirementsKHR(vr_engine.appState.Instance, systemId, &graphicsRequirements));
vr_engine.appState.MainThreadTid = gettid();
vr_engine.appState.SystemId = systemId;
vr_engine.java = java;
vr_initialized = qtrue;
return &vr_engine;
}
void VR_InitCvars( void )
@ -193,57 +271,89 @@ void VR_InitCvars( void )
Cvar_Get ("vr_button_map_SECONDARYGRIP", "+weapon_stabilise", CVAR_ARCHIVE); // Weapon stabilisation
Cvar_Get ("vr_button_map_SECONDARYGRIP_ALT", "", CVAR_ARCHIVE); // unmapped
Cvar_Get ("vr_button_map_PRIMARYGRIP_ALT", "", CVAR_ARCHIVE); // unmapped
vr.menuYaw = 0;
vr.recenterYaw = 0;
}
void VR_Destroy( engine_t* engine )
{
if (engine == &vr_engine) {
vrapi_Shutdown();
}
if (engine == &vr_engine) {
xrDestroyInstance(engine->appState.Instance);
ovrApp_Destroy(&engine->appState);
}
}
void VR_EnterVR( engine_t* engine, ovrJava java ) {
if (!engine->ovr) {
ovrModeParms modeParams = vrapi_DefaultModeParms(&java);
modeParams.Display = (size_t)eglGetCurrentDisplay();
modeParams.WindowSurface = (size_t)eglGetCurrentSurface(EGL_DRAW);
modeParams.ShareContext = (size_t)eglGetCurrentContext();
engine->ovr = vrapi_EnterVrMode(&modeParams);
engine->frameIndex = 0;
if (engine->appState.Session) {
Com_Printf("VR_EnterVR called with existing session");
return;
}
vrapi_SetTrackingSpace(engine->ovr, VRAPI_TRACKING_SPACE_LOCAL_FLOOR);
// Create the OpenXR Session.
XrGraphicsBindingOpenGLESAndroidKHR graphicsBindingAndroidGLES = {};
graphicsBindingAndroidGLES.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR;
graphicsBindingAndroidGLES.next = NULL;
graphicsBindingAndroidGLES.display = eglGetCurrentDisplay();
graphicsBindingAndroidGLES.config = eglGetCurrentSurface(EGL_DRAW);
graphicsBindingAndroidGLES.context = eglGetCurrentContext();
vrapi_SetClockLevels(engine->ovr, 4, 4);
}
XrSessionCreateInfo sessionCreateInfo = {};
memset(&sessionCreateInfo, 0, sizeof(sessionCreateInfo));
sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO;
sessionCreateInfo.next = &graphicsBindingAndroidGLES;
sessionCreateInfo.createFlags = 0;
sessionCreateInfo.systemId = engine->appState.SystemId;
XrResult initResult;
OXR(initResult = xrCreateSession(engine->appState.Instance, &sessionCreateInfo, &engine->appState.Session));
if (initResult != XR_SUCCESS) {
ALOGE("Failed to create XR session: %d.", initResult);
exit(1);
}
// Create a space to the first path
XrReferenceSpaceCreateInfo spaceCreateInfo = {};
spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f;
OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.HeadSpace));
}
void VR_LeaveVR( engine_t* engine ) {
if (engine->ovr) {
vrapi_LeaveVrMode(engine->ovr);
engine->ovr = NULL;
}
if (engine->appState.Session) {
OXR(xrDestroySpace(engine->appState.HeadSpace));
// StageSpace is optional.
if (engine->appState.StageSpace != XR_NULL_HANDLE) {
OXR(xrDestroySpace(engine->appState.StageSpace));
}
OXR(xrDestroySpace(engine->appState.FakeStageSpace));
engine->appState.CurrentSpace = XR_NULL_HANDLE;
OXR(xrDestroySession(engine->appState.Session));
engine->appState.Session = NULL;
}
}
engine_t* VR_GetEngine( void ) {
return &vr_engine;
}
bool VR_isPauseable( void )
int VR_isPauseable( void )
{
return (bool)( ( clc.state == CA_ACTIVE) && !Cvar_VariableValue ("cl_paused") );
return ( clc.state == CA_ACTIVE) && !Cvar_VariableValue ("cl_paused");
}
bool VR_useScreenLayer( void )
int VR_useScreenLayer( void )
{
//intermission is never full screen
if ( cl.snap.ps.pm_type == PM_INTERMISSION )
{
return qfalse;
return 0;
}
int keyCatcher = Key_GetCatcher( );
return (bool)( clc.state == CA_CINEMATIC ||
return ( clc.state == CA_CINEMATIC ||
( keyCatcher & (KEYCATCH_UI | KEYCATCH_CONSOLE) ));
}
//#endif

View file

@ -12,8 +12,8 @@ void VR_EnterVR( engine_t* engine, ovrJava java );
void VR_LeaveVR( engine_t* engine );
engine_t* VR_GetEngine( void );
bool VR_isPauseable( void );
bool VR_useScreenLayer( void );
int VR_useScreenLayer( void );
int VR_isPauseable( void );
float radians(float deg);

View file

@ -64,6 +64,7 @@ typedef struct {
int *menuCursorY;
qboolean menuLeftHanded;
float recenterYaw;
float superSampling;
} vr_clientinfo_t;

View file

@ -2,13 +2,10 @@
//#if __ANDROID__
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../client/keycodes.h"
#include "../client/client.h"
#include "vr_base.h"
#include "../VrApi/Include/VrApi_Input.h"
#include "../VrApi/Include/VrApi_Helpers.h"
#include "vr_clientinfo.h"
#include <unistd.h>
@ -20,6 +17,33 @@
# include <SDL.h>
#endif
//OpenXR
XrPath leftHandPath;
XrPath rightHandPath;
XrAction handPoseLeftAction;
XrAction handPoseRightAction;
XrAction indexLeftAction;
XrAction indexRightAction;
XrAction menuAction;
XrAction buttonAAction;
XrAction buttonBAction;
XrAction buttonXAction;
XrAction buttonYAction;
XrAction gripLeftAction;
XrAction gripRightAction;
XrAction moveOnLeftJoystickAction;
XrAction moveOnRightJoystickAction;
XrAction thumbstickLeftClickAction;
XrAction thumbstickRightClickAction;
XrAction vibrateLeftFeedback;
XrAction vibrateRightFeedback;
XrActionSet runningActionSet;
XrSpace leftControllerAimSpace = XR_NULL_HANDLE;
XrSpace rightControllerAimSpace = XR_NULL_HANDLE;
qboolean actionsAttached = qfalse;
qboolean inputInitialized = qfalse;
qboolean useSimpleProfile = qfalse;
enum {
VR_TOUCH_AXIS_UP = 1 << 0,
VR_TOUCH_AXIS_UPRIGHT = 1 << 1,
@ -92,7 +116,7 @@ void rotateAboutOrigin(float x, float y, float rotation, vec2_t out)
out[1] = cosf(DEG2RAD(-rotation)) * y - sinf(DEG2RAD(-rotation)) * x;
}
static ovrVector3f normalizeVec(ovrVector3f vec) {
XrVector3f normalizeVec(XrVector3f vec) {
//NOTE: leave w-component untouched
//@@const float EPSILON = 0.000001f;
float xxyyzz = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z;
@ -100,7 +124,7 @@ static ovrVector3f normalizeVec(ovrVector3f vec) {
//@@ return *this; // do nothing if it is zero vector
//float invLength = invSqrt(xxyyzz);
ovrVector3f result;
XrVector3f result;
float invLength = 1.0f / sqrtf(xxyyzz);
result.x = vec.x * invLength;
result.y = vec.y * invLength;
@ -123,7 +147,7 @@ void NormalizeAngles(vec3_t angles)
while (angles[2] < -180) angles[2] += 360;
}
void GetAnglesFromVectors(const ovrVector3f forward, const ovrVector3f right, const ovrVector3f up, vec3_t angles)
void GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up, vec3_t angles)
{
float sr, sp, sy, cr, cp, cy;
@ -172,7 +196,7 @@ void GetAnglesFromVectors(const ovrVector3f forward, const ovrVector3f right, co
NormalizeAngles(angles);
}
void QuatToYawPitchRoll(ovrQuatf q, vec3_t rotation, vec3_t out) {
void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out) {
ovrMatrix4f mat = ovrMatrix4f_CreateFromQuaternion( &q );
@ -182,21 +206,21 @@ void QuatToYawPitchRoll(ovrQuatf q, vec3_t rotation, vec3_t out) {
mat = ovrMatrix4f_Multiply(&mat, &rot);
}
ovrVector4f v1 = {0, 0, -1, 0};
ovrVector4f v2 = {1, 0, 0, 0};
ovrVector4f v3 = {0, 1, 0, 0};
XrVector4f v1 = {0, 0, -1, 0};
XrVector4f v2 = {1, 0, 0, 0};
XrVector4f v3 = {0, 1, 0, 0};
ovrVector4f forwardInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v1);
ovrVector4f rightInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v2);
ovrVector4f upInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v3);
XrVector4f forwardInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v1);
XrVector4f rightInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v2);
XrVector4f upInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v3);
ovrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y};
ovrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y};
ovrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y};
XrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y};
XrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y};
XrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y};
ovrVector3f forwardNormal = normalizeVec(forward);
ovrVector3f rightNormal = normalizeVec(right);
ovrVector3f upNormal = normalizeVec(up);
XrVector3f forwardNormal = normalizeVec(forward);
XrVector3f rightNormal = normalizeVec(right);
XrVector3f upNormal = normalizeVec(up);
GetAnglesFromVectors(forwardNormal, rightNormal, upNormal, out);
}
@ -204,7 +228,6 @@ void QuatToYawPitchRoll(ovrQuatf q, vec3_t rotation, vec3_t out) {
//0 = left, 1 = right
float vibration_channel_duration[2] = {0.0f, 0.0f};
float vibration_channel_intensity[2] = {0.0f, 0.0f};
ovrDeviceID controllerIDs[2];
void VR_Vibrate( int duration, int chan, float intensity )
{
@ -235,8 +258,19 @@ static void VR_processHaptics() {
for (int i = 0; i < 2; ++i) {
if (vibration_channel_duration[i] > 0.0f ||
vibration_channel_duration[i] == -1.0f) {
vrapi_SetHapticVibrationSimple(VR_GetEngine()->ovr, controllerIDs[i],
vibration_channel_intensity[i]);
// fire haptics using output action
XrHapticVibration vibration = {};
vibration.type = XR_TYPE_HAPTIC_VIBRATION;
vibration.next = NULL;
vibration.amplitude = vibration_channel_intensity[i];
vibration.duration = ToXrTime(vibration_channel_duration[i]);
vibration.frequency = 3000;
XrHapticActionInfo hapticActionInfo = {};
hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO;
hapticActionInfo.next = NULL;
hapticActionInfo.action = i == 0 ? vibrateLeftFeedback : vibrateRightFeedback;
OXR(xrApplyHapticFeedback(VR_GetEngine()->appState.Session, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration));
if (vibration_channel_duration[i] != -1.0f) {
vibration_channel_duration[i] -= frametime;
@ -247,7 +281,12 @@ static void VR_processHaptics() {
}
}
} else {
vrapi_SetHapticVibrationSimple(VR_GetEngine()->ovr, controllerIDs[i], 0.0f);
// Stop haptics
XrHapticActionInfo hapticActionInfo = {};
hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO;
hapticActionInfo.next = NULL;
hapticActionInfo.action = i == 0 ? vibrateLeftFeedback : vibrateRightFeedback;
OXR(xrStopHapticFeedback(VR_GetEngine()->appState.Session, &hapticActionInfo));
}
}
}
@ -468,50 +507,355 @@ void VR_HapticEvent(const char* event, int position, int flags, int intensity, f
}
}
XrSpace CreateActionSpace(XrAction poseAction, XrPath subactionPath) {
XrActionSpaceCreateInfo asci = {};
asci.type = XR_TYPE_ACTION_SPACE_CREATE_INFO;
asci.action = poseAction;
asci.poseInActionSpace.orientation.w = 1.0f;
asci.subactionPath = subactionPath;
XrSpace actionSpace = XR_NULL_HANDLE;
OXR(xrCreateActionSpace(VR_GetEngine()->appState.Session, &asci, &actionSpace));
return actionSpace;
}
XrActionSuggestedBinding ActionSuggestedBinding(XrAction action, const char* bindingString) {
XrActionSuggestedBinding asb;
asb.action = action;
XrPath bindingPath;
OXR(xrStringToPath(VR_GetEngine()->appState.Instance, bindingString, &bindingPath));
asb.binding = bindingPath;
return asb;
}
XrActionSet CreateActionSet(int priority, const char* name, const char* localizedName) {
XrActionSetCreateInfo asci = {};
asci.type = XR_TYPE_ACTION_SET_CREATE_INFO;
asci.next = NULL;
asci.priority = priority;
strcpy(asci.actionSetName, name);
strcpy(asci.localizedActionSetName, localizedName);
XrActionSet actionSet = XR_NULL_HANDLE;
OXR(xrCreateActionSet(VR_GetEngine()->appState.Instance, &asci, &actionSet));
return actionSet;
}
XrAction CreateAction(
XrActionSet actionSet,
XrActionType type,
const char* actionName,
const char* localizedName,
int countSubactionPaths,
XrPath* subactionPaths) {
ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths);
XrActionCreateInfo aci = {};
aci.type = XR_TYPE_ACTION_CREATE_INFO;
aci.next = NULL;
aci.actionType = type;
if (countSubactionPaths > 0) {
aci.countSubactionPaths = countSubactionPaths;
aci.subactionPaths = subactionPaths;
}
strcpy(aci.actionName, actionName);
strcpy(aci.localizedActionName, localizedName ? localizedName : actionName);
XrAction action = XR_NULL_HANDLE;
OXR(xrCreateAction(actionSet, &aci, &action));
return action;
}
qboolean ActionPoseIsActive(XrAction action, XrPath subactionPath) {
XrActionStateGetInfo getInfo = {};
getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
getInfo.action = action;
getInfo.subactionPath = subactionPath;
XrActionStatePose state = {};
state.type = XR_TYPE_ACTION_STATE_POSE;
OXR(xrGetActionStatePose(VR_GetEngine()->appState.Session, &getInfo, &state));
return state.isActive != XR_FALSE;
}
XrActionStateFloat GetActionStateFloat(XrAction action) {
XrActionStateGetInfo getInfo = {};
getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
getInfo.action = action;
XrActionStateFloat state = {};
state.type = XR_TYPE_ACTION_STATE_FLOAT;
OXR(xrGetActionStateFloat(VR_GetEngine()->appState.Session, &getInfo, &state));
return state;
}
XrActionStateBoolean GetActionStateBoolean(XrAction action) {
XrActionStateGetInfo getInfo = {};
getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
getInfo.action = action;
XrActionStateBoolean state = {};
state.type = XR_TYPE_ACTION_STATE_BOOLEAN;
OXR(xrGetActionStateBoolean(VR_GetEngine()->appState.Session, &getInfo, &state));
return state;
}
XrActionStateVector2f GetActionStateVector2(XrAction action) {
XrActionStateGetInfo getInfo = {};
getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
getInfo.action = action;
XrActionStateVector2f state = {};
state.type = XR_TYPE_ACTION_STATE_VECTOR2F;
OXR(xrGetActionStateVector2f(VR_GetEngine()->appState.Session, &getInfo, &state));
return state;
}
void IN_VRInit( void )
{
if (inputInitialized)
return;
memset(&vr, 0, sizeof(vr));
engine_t *engine = VR_GetEngine();
callbackClass = (*engine->java.Env)->GetObjectClass(engine->java.Env, engine->java.ActivityObject);
android_haptic_event = (*engine->java.Env)->GetMethodID(engine->java.Env, callbackClass, "haptic_event","(Ljava/lang/String;IIIFF)V");
// Actions
runningActionSet = CreateActionSet(1, "running_action_set", "Action Set used on main loop");
indexLeftAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "index_left", "Index left", 0, NULL);
indexRightAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "index_right", "Index right", 0, NULL);
menuAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "menu_action", "Menu", 0, NULL);
buttonAAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_a", "Button A", 0, NULL);
buttonBAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_b", "Button B", 0, NULL);
buttonXAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_x", "Button X", 0, NULL);
buttonYAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_y", "Button Y", 0, NULL);
gripLeftAction = CreateAction(runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "grip_left", "Grip left", 0, NULL);
gripRightAction = CreateAction(runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "grip_right", "Grip right", 0, NULL);
moveOnLeftJoystickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_VECTOR2F_INPUT, "move_on_left_joy", "Move on left Joy", 0, NULL);
moveOnRightJoystickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_VECTOR2F_INPUT, "move_on_right_joy", "Move on right Joy", 0, NULL);
thumbstickLeftClickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumbstick_left", "Thumbstick left", 0, NULL);
thumbstickRightClickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumbstick_right", "Thumbstick right", 0, NULL);
vibrateLeftFeedback = CreateAction(runningActionSet, XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_left_feedback", "Vibrate Left Controller Feedback", 0, NULL);
vibrateRightFeedback = CreateAction(runningActionSet, XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_right_feedback", "Vibrate Right Controller Feedback", 0, NULL);
OXR(xrStringToPath(engine->appState.Instance, "/user/hand/left", &leftHandPath));
OXR(xrStringToPath(engine->appState.Instance, "/user/hand/right", &rightHandPath));
handPoseLeftAction = CreateAction(runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "hand_pose_left", NULL, 1, &leftHandPath);
handPoseRightAction = CreateAction(runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "hand_pose_right", NULL, 1, &rightHandPath);
XrPath interactionProfilePath = XR_NULL_PATH;
XrPath interactionProfilePathTouch = XR_NULL_PATH;
XrPath interactionProfilePathKHRSimple = XR_NULL_PATH;
OXR(xrStringToPath(engine->appState.Instance, "/interaction_profiles/oculus/touch_controller", &interactionProfilePathTouch));
OXR(xrStringToPath(engine->appState.Instance, "/interaction_profiles/khr/simple_controller", &interactionProfilePathKHRSimple));
// Toggle this to force simple as a first choice, otherwise use it as a last resort
if (useSimpleProfile) {
ALOGV("xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller");
interactionProfilePath = interactionProfilePathKHRSimple;
} else {
// Query Set
XrActionSet queryActionSet = CreateActionSet(1, "query_action_set", "Action Set used to query device caps");
XrAction dummyAction = CreateAction(queryActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "dummy_action", "Dummy Action", 0, NULL);
// Map bindings
XrActionSuggestedBinding bindings[1];
int currBinding = 0;
bindings[currBinding++] = ActionSuggestedBinding(dummyAction, "/user/hand/right/input/system/click");
XrInteractionProfileSuggestedBinding suggestedBindings = {};
suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
suggestedBindings.next = NULL;
suggestedBindings.suggestedBindings = bindings;
suggestedBindings.countSuggestedBindings = currBinding;
// Try all
suggestedBindings.interactionProfile = interactionProfilePathTouch;
XrResult suggestTouchResult = xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings);
OXR(suggestTouchResult);
if (XR_SUCCESS == suggestTouchResult) {
ALOGV("xrSuggestInteractionProfileBindings found bindings for QUEST controller");
interactionProfilePath = interactionProfilePathTouch;
}
if (interactionProfilePath == XR_NULL_PATH) {
// Simple as a fallback
bindings[0] = ActionSuggestedBinding(dummyAction, "/user/hand/right/input/select/click");
suggestedBindings.interactionProfile = interactionProfilePathKHRSimple;
XrResult suggestKHRSimpleResult = xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings);
OXR(suggestKHRSimpleResult);
if (XR_SUCCESS == suggestKHRSimpleResult) {
ALOGV("xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller");
interactionProfilePath = interactionProfilePathKHRSimple;
} else {
ALOGE("xrSuggestInteractionProfileBindings did NOT find any bindings.");
assert(qfalse);
}
}
}
// Action creation
{
// Map bindings
XrActionSuggestedBinding bindings[32]; // large enough for all profiles
int currBinding = 0;
{
if (interactionProfilePath == interactionProfilePathTouch) {
bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/trigger");
bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/trigger");
bindings[currBinding++] = ActionSuggestedBinding(menuAction, "/user/hand/left/input/menu/click");
bindings[currBinding++] = ActionSuggestedBinding(buttonXAction, "/user/hand/left/input/x/click");
bindings[currBinding++] = ActionSuggestedBinding(buttonYAction, "/user/hand/left/input/y/click");
bindings[currBinding++] = ActionSuggestedBinding(buttonAAction, "/user/hand/right/input/a/click");
bindings[currBinding++] = ActionSuggestedBinding(buttonBAction, "/user/hand/right/input/b/click");
bindings[currBinding++] = ActionSuggestedBinding(gripLeftAction, "/user/hand/left/input/squeeze/value");
bindings[currBinding++] = ActionSuggestedBinding(gripRightAction, "/user/hand/right/input/squeeze/value");
bindings[currBinding++] = ActionSuggestedBinding(moveOnLeftJoystickAction, "/user/hand/left/input/thumbstick");
bindings[currBinding++] = ActionSuggestedBinding(moveOnRightJoystickAction, "/user/hand/right/input/thumbstick");
bindings[currBinding++] = ActionSuggestedBinding(thumbstickLeftClickAction, "/user/hand/left/input/thumbstick/click");
bindings[currBinding++] = ActionSuggestedBinding(thumbstickRightClickAction, "/user/hand/right/input/thumbstick/click");
bindings[currBinding++] = ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic");
bindings[currBinding++] = ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic");
bindings[currBinding++] = ActionSuggestedBinding(handPoseLeftAction, "/user/hand/left/input/aim/pose");
bindings[currBinding++] = ActionSuggestedBinding(handPoseRightAction, "/user/hand/right/input/aim/pose");
}
if (interactionProfilePath == interactionProfilePathKHRSimple) {
bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/select/click");
bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/select/click");
bindings[currBinding++] = ActionSuggestedBinding(buttonAAction, "/user/hand/left/input/menu/click");
bindings[currBinding++] = ActionSuggestedBinding(buttonXAction, "/user/hand/right/input/menu/click");
bindings[currBinding++] = ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic");
bindings[currBinding++] = ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic");
bindings[currBinding++] = ActionSuggestedBinding(handPoseLeftAction, "/user/hand/left/input/aim/pose");
bindings[currBinding++] = ActionSuggestedBinding(handPoseRightAction, "/user/hand/right/input/aim/pose");
}
}
XrInteractionProfileSuggestedBinding suggestedBindings = {};
suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
suggestedBindings.next = NULL;
suggestedBindings.interactionProfile = interactionProfilePath;
suggestedBindings.suggestedBindings = bindings;
suggestedBindings.countSuggestedBindings = currBinding;
OXR(xrSuggestInteractionProfileBindings(engine->appState.Instance, &suggestedBindings));
// Enumerate actions
XrPath actionPathsBuffer[32];
char stringBuffer[256];
XrAction actionsToEnumerate[] = {
indexLeftAction,
indexRightAction,
menuAction,
buttonAAction,
buttonBAction,
buttonXAction,
buttonYAction,
gripLeftAction,
gripRightAction,
moveOnLeftJoystickAction,
moveOnRightJoystickAction,
thumbstickLeftClickAction,
thumbstickRightClickAction,
vibrateLeftFeedback,
vibrateRightFeedback,
handPoseLeftAction,
handPoseRightAction
};
for (size_t i = 0; i < sizeof(actionsToEnumerate) / sizeof(actionsToEnumerate[0]); ++i) {
XrBoundSourcesForActionEnumerateInfo enumerateInfo = {};
enumerateInfo.type = XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO;
enumerateInfo.next = NULL;
enumerateInfo.action = actionsToEnumerate[i];
// Get Count
uint32_t countOutput = 0;
OXR(xrEnumerateBoundSourcesForAction(
engine->appState.Session, &enumerateInfo, 0 /* request size */, &countOutput, NULL));
ALOGV(
"xrEnumerateBoundSourcesForAction action=%lld count=%u",
(long long)enumerateInfo.action,
countOutput);
if (countOutput < 32) {
OXR(xrEnumerateBoundSourcesForAction(
engine->appState.Session, &enumerateInfo, 32, &countOutput, actionPathsBuffer));
for (uint32_t a = 0; a < countOutput; ++a) {
XrInputSourceLocalizedNameGetInfo nameGetInfo = {};
nameGetInfo.type = XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO;
nameGetInfo.next = NULL;
nameGetInfo.sourcePath = actionPathsBuffer[a];
nameGetInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT |
XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT |
XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT;
uint32_t stringCount = 0u;
OXR(xrGetInputSourceLocalizedName(
engine->appState.Session, &nameGetInfo, 0, &stringCount, NULL));
if (stringCount < 256) {
OXR(xrGetInputSourceLocalizedName(
engine->appState.Session, &nameGetInfo, 256, &stringCount, stringBuffer));
char pathStr[256];
uint32_t strLen = 0;
OXR(xrPathToString(
engine->appState.Instance,
actionPathsBuffer[a],
(uint32_t)sizeof(pathStr),
&strLen,
pathStr));
ALOGV(
" -> path = %lld `%s` -> `%s`",
(long long)actionPathsBuffer[a],
pathStr,
stringBuffer);
}
}
}
}
}
inputInitialized = qtrue;
}
static void IN_VRController( qboolean isRightController, ovrTracking remoteTracking )
static void IN_VRController( qboolean isRightController, XrPosef pose )
{
//Set gun angles - We need to calculate all those we might need (including adjustments) for the client to then take its pick
vec3_t rotation = {0};
if (isRightController == (vr_righthanded->integer != 0))
{
//Set gun angles - We need to calculate all those we might need (including adjustments) for the client to then take its pick
vec3_t rotation = {0};
rotation[PITCH] = vr_weaponPitch->value;
QuatToYawPitchRoll(remoteTracking.HeadPose.Pose.Orientation, rotation, vr.weaponangles);
VectorSubtract(vr.weaponangles_last, vr.weaponangles, vr.weaponangles_delta);
VectorCopy(vr.weaponangles, vr.weaponangles_last);
///Weapon location relative to view
vr.weaponposition[0] = remoteTracking.HeadPose.Pose.Position.x;
vr.weaponposition[1] = remoteTracking.HeadPose.Pose.Position.y + vr_heightAdjust->value;
vr.weaponposition[2] = remoteTracking.HeadPose.Pose.Position.z;
VectorCopy(vr.weaponoffset_last[1], vr.weaponoffset_last[0]);
VectorCopy(vr.weaponoffset, vr.weaponoffset_last[1]);
VectorSubtract(vr.weaponposition, vr.hmdposition, vr.weaponoffset);
} else {
vec3_t rotation = {0};
QuatToYawPitchRoll(remoteTracking.HeadPose.Pose.Orientation, rotation, vr.offhandangles2); // used for off-hand direction mode
{
//Set gun angles - We need to calculate all those we might need (including adjustments) for the client to then take its pick
rotation[PITCH] = vr_weaponPitch->value;
QuatToYawPitchRoll(remoteTracking.HeadPose.Pose.Orientation, rotation, vr.offhandangles);
QuatToYawPitchRoll(pose.orientation, rotation, vr.weaponangles);
VectorSubtract(vr.weaponangles_last, vr.weaponangles, vr.weaponangles_delta);
VectorCopy(vr.weaponangles, vr.weaponangles_last);
///Weapon location relative to view
vr.weaponposition[0] = pose.position.x;
vr.weaponposition[1] = pose.position.y + vr_heightAdjust->value;
vr.weaponposition[2] = pose.position.z;
VectorCopy(vr.weaponoffset_last[1], vr.weaponoffset_last[0]);
VectorCopy(vr.weaponoffset, vr.weaponoffset_last[1]);
VectorSubtract(vr.weaponposition, vr.hmdposition, vr.weaponoffset);
} else {
QuatToYawPitchRoll(pose.orientation, rotation, vr.offhandangles2); // used for off-hand direction mode
rotation[PITCH] = vr_weaponPitch->value;
QuatToYawPitchRoll(pose.orientation, rotation, vr.offhandangles);
///location relative to view
vr.offhandposition[0] = remoteTracking.HeadPose.Pose.Position.x;
vr.offhandposition[1] = remoteTracking.HeadPose.Pose.Position.y + vr_heightAdjust->value;
vr.offhandposition[2] = remoteTracking.HeadPose.Pose.Position.z;
vr.offhandposition[0] = pose.position.x;
vr.offhandposition[1] = pose.position.y + vr_heightAdjust->value;
vr.offhandposition[2] = pose.position.z;
VectorCopy(vr.offhandoffset_last[1], vr.offhandoffset_last[0]);
VectorCopy(vr.offhandoffset, vr.offhandoffset_last[1]);
VectorSubtract(vr.offhandposition, vr.hmdposition, vr.offhandoffset);
}
}
if (vr.virtual_screen || cl.snap.ps.pm_type == PM_INTERMISSION)
{
@ -750,7 +1094,8 @@ static void IN_VRJoystick( qboolean isRightController, float joystickX, float jo
else
{
//Positional movement speed correction for when we are not hitting target framerate
int refresh = vrapi_GetSystemPropertyInt(&(VR_GetEngine()->java), VRAPI_SYS_PROP_DISPLAY_REFRESH_RATE);
float refresh;
VR_GetEngine()->appState.pfnGetDisplayRefreshRate(VR_GetEngine()->appState.Session, &refresh);
float multiplier = (float)((1000.0 / refresh) / (in_vrEventTime - lastframetime));
float factor = (refresh / 72.0F) * 10.0f; // adjust positional factor based on refresh rate
@ -967,7 +1312,6 @@ static void IN_VRButtons( qboolean isRightController, uint32_t buttons )
} else {
IN_HandleInactiveInput(&controller->buttons, ovrButton_Y, "Y", 0, qfalse);
}
}
void IN_VRInputFrame( void )
@ -977,27 +1321,17 @@ void IN_VRInputFrame( void )
memset(&rightController, 0, sizeof(rightController));
controllerInit = qtrue;
}
engine_t* engine = VR_GetEngine();
ovrMobile* ovr = VR_GetEngine()->ovr;
if (!ovr) {
return;
}
ovrResult result;
if (vr_extralatencymode != NULL &&
vr_extralatencymode->integer) {
result = vrapi_SetExtraLatencyMode(VR_GetEngine()->ovr, VRAPI_EXTRA_LATENCY_MODE_ON);
assert(result == VRAPI_INITIALIZE_SUCCESS);
//TODO:vrapi_SetExtraLatencyMode(VR_GetEngine()->ovr, VRAPI_EXTRA_LATENCY_MODE_ON);
}
if (vr_refreshrate != NULL && vr_refreshrate->integer)
{
vrapi_SetDisplayRefreshRate(VR_GetEngine()->ovr, (float)vr_refreshrate->integer);
if (vr_refreshrate != NULL && vr_refreshrate->integer) {
OXR(engine->appState.pfnRequestDisplayRefreshRate(engine->appState.Session, (float)vr_refreshrate->integer));
}
result = vrapi_SetClockLevels(VR_GetEngine()->ovr, 4, 4);
assert(result == VRAPI_INITIALIZE_SUCCESS);
vr.virtual_screen = VR_useScreenLayer();
VR_processHaptics();
@ -1005,90 +1339,134 @@ void IN_VRInputFrame( void )
//trigger frame tick for haptics
VR_HapticEvent("frame_tick", 0, 0, 0, 0, 0);
{
// We extract Yaw, Pitch, Roll instead of directly using the orientation
// to allow "additional" yaw manipulation with mouse/controller.
const ovrQuatf quatHmd = VR_GetEngine()->tracking.HeadPose.Pose.Orientation;
const ovrVector3f positionHmd = VR_GetEngine()->tracking.HeadPose.Pose.Position;
vec3_t rotation = {0, 0, 0};
QuatToYawPitchRoll(quatHmd, rotation, vr.hmdorientation);
VectorSet(vr.hmdposition, positionHmd.x, positionHmd.y + vr_heightAdjust->value, positionHmd.z);
if (leftControllerAimSpace == XR_NULL_HANDLE) {
leftControllerAimSpace = CreateActionSpace(handPoseLeftAction, leftHandPath);
}
if (rightControllerAimSpace == XR_NULL_HANDLE) {
rightControllerAimSpace = CreateActionSpace(handPoseRightAction, rightHandPath);
}
//Position
VectorSubtract(vr.hmdposition_last, vr.hmdposition, vr.hmdposition_delta);
//button mapping
uint32_t lButtons = 0;
if (GetActionStateBoolean(menuAction).currentState) lButtons |= ovrButton_Enter;
if (GetActionStateBoolean(buttonXAction).currentState) lButtons |= ovrButton_X;
if (GetActionStateBoolean(buttonYAction).currentState) lButtons |= ovrButton_Y;
if (GetActionStateFloat(gripLeftAction).currentState > 0.5f) lButtons |= ovrButton_GripTrigger;
if (GetActionStateBoolean(thumbstickLeftClickAction).currentState) lButtons |= ovrButton_LThumb;
IN_VRButtons(qfalse, lButtons);
uint32_t rButtons = 0;
if (GetActionStateBoolean(buttonAAction).currentState) rButtons |= ovrButton_A;
if (GetActionStateBoolean(buttonBAction).currentState) rButtons |= ovrButton_B;
if (GetActionStateFloat(gripRightAction).currentState > 0.5f) rButtons |= ovrButton_GripTrigger;
if (GetActionStateBoolean(thumbstickRightClickAction).currentState) rButtons |= ovrButton_RThumb;
IN_VRButtons(qtrue, rButtons);
//Keep this for our records
VectorCopy(vr.hmdposition, vr.hmdposition_last);
//index finger click
XrActionStateBoolean indexState;
indexState = GetActionStateBoolean(indexLeftAction);
IN_VRTriggers(qfalse, indexState.currentState ? 1 : 0);
indexState = GetActionStateBoolean(indexRightAction);
IN_VRTriggers(qtrue, indexState.currentState ? 1 : 0);
//Orientation
VectorSubtract(vr.hmdorientation_last, vr.hmdorientation, vr.hmdorientation_delta);
//Keep this for our records
VectorCopy(vr.hmdorientation, vr.hmdorientation_last);
// View yaw delta
const float clientview_yaw = vr.clientviewangles[YAW] - vr.hmdorientation[YAW];
vr.clientview_yaw_delta = vr.clientview_yaw_last - clientview_yaw;
vr.clientview_yaw_last = clientview_yaw;
}
ovrInputCapabilityHeader capsHeader;
uint32_t index = 0;
for (;;) {
ovrResult enumResult = vrapi_EnumerateInputDevices(ovr, index, &capsHeader);
if (enumResult < 0) {
break;
}
++index;
if (capsHeader.Type != ovrControllerType_TrackedRemote) {
continue;
}
ovrInputTrackedRemoteCapabilities caps;
caps.Header = capsHeader;
ovrResult capsResult = vrapi_GetInputDeviceCapabilities(ovr, &caps.Header);
if (capsResult < 0) {
continue;
}
ovrInputStateTrackedRemote state;
state.Header.ControllerType = ovrControllerType_TrackedRemote;
ovrResult stateResult = vrapi_GetCurrentInputState(ovr, capsHeader.DeviceID, &state.Header);
if (stateResult < 0) {
continue;
}
ovrTracking remoteTracking;
stateResult = vrapi_GetInputTrackingState(ovr, capsHeader.DeviceID, VR_GetEngine()->predictedDisplayTime,
&remoteTracking);
if (stateResult < 0) {
continue;
}
qboolean isRight;
vrController_t* controller;
if (caps.ControllerCapabilities & ovrControllerCaps_LeftHand) {
isRight = qfalse;
controller = &leftController;
controllerIDs[0] = capsHeader.DeviceID;
} else if (caps.ControllerCapabilities & ovrControllerCaps_RightHand) {
isRight = qtrue;
controller = &rightController;
controllerIDs[1] = capsHeader.DeviceID;
}
else {
continue;
}
IN_VRButtons(isRight, state.Buttons);
IN_VRController(isRight, remoteTracking);
IN_VRJoystick(isRight, state.Joystick.x, state.Joystick.y);
IN_VRTriggers(isRight, state.IndexTrigger);
}
//thumbstick
XrActionStateVector2f moveJoystickState;
moveJoystickState = GetActionStateVector2(moveOnLeftJoystickAction);
IN_VRJoystick(qfalse, moveJoystickState.currentState.x, moveJoystickState.currentState.y);
moveJoystickState = GetActionStateVector2(moveOnRightJoystickAction);
IN_VRJoystick(qtrue, moveJoystickState.currentState.x, moveJoystickState.currentState.y);
lastframetime = in_vrEventTime;
in_vrEventTime = Sys_Milliseconds( );
}
void IN_VRSyncActions( void )
{
engine_t* engine = VR_GetEngine();
// Attach to session
if (!actionsAttached) {
XrSessionActionSetsAttachInfo attachInfo = {};
attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
attachInfo.next = NULL;
attachInfo.countActionSets = 1;
attachInfo.actionSets = &runningActionSet;
OXR(xrAttachSessionActionSets(engine->appState.Session, &attachInfo));
actionsAttached = qtrue;
}
// sync action data
XrActiveActionSet activeActionSet = {};
activeActionSet.actionSet = runningActionSet;
activeActionSet.subactionPath = XR_NULL_PATH;
XrActionsSyncInfo syncInfo = {};
syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO;
syncInfo.next = NULL;
syncInfo.countActiveActionSets = 1;
syncInfo.activeActionSets = &activeActionSet;
OXR(xrSyncActions(engine->appState.Session, &syncInfo));
// query input action states
XrActionStateGetInfo getInfo = {};
getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
getInfo.next = NULL;
getInfo.subactionPath = XR_NULL_PATH;
}
void IN_VRUpdateControllers( XrPosef xfStageFromHead, float predictedDisplayTime )
{
engine_t* engine = VR_GetEngine();
//get controller poses
XrAction controller[] = {handPoseLeftAction, handPoseRightAction};
XrPath subactionPath[] = {leftHandPath, rightHandPath};
XrSpace controllerSpace[] = {leftControllerAimSpace, rightControllerAimSpace};
for (int i = 0; i < 2; i++) {
if (ActionPoseIsActive(controller[i], subactionPath[i])) {
XrSpaceLocation loc = {};
loc.type = XR_TYPE_SPACE_LOCATION;
OXR(xrLocateSpace(controllerSpace[i], engine->appState.HeadSpace, predictedDisplayTime, &loc));
engine->appState.TrackedController[i].Active = (loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0;
engine->appState.TrackedController[i].Pose = XrPosef_Multiply(xfStageFromHead, loc.pose);
} else {
ovrTrackedController_Clear(&engine->appState.TrackedController[i]);
}
}
//apply controller poses
if (engine->appState.TrackedController[0].Active)
IN_VRController(qfalse, engine->appState.TrackedController[0].Pose);
if (engine->appState.TrackedController[1].Active)
IN_VRController(qtrue, engine->appState.TrackedController[1].Pose);
}
void IN_VRUpdateHMD( XrPosef xfStageFromHead )
{
// We extract Yaw, Pitch, Roll instead of directly using the orientation
// to allow "additional" yaw manipulation with mouse/controller.
const XrQuaternionf quatHmd = xfStageFromHead.orientation;
const XrVector3f positionHmd = xfStageFromHead.position;
vec3_t rotation = {0, 0, 0};
QuatToYawPitchRoll(quatHmd, rotation, vr.hmdorientation);
VectorSet(vr.hmdposition, positionHmd.x, positionHmd.y + vr_heightAdjust->value, positionHmd.z);
//Position
VectorSubtract(vr.hmdposition_last, vr.hmdposition, vr.hmdposition_delta);
//Keep this for our records
VectorCopy(vr.hmdposition, vr.hmdposition_last);
//Orientation
VectorSubtract(vr.hmdorientation_last, vr.hmdorientation, vr.hmdorientation_delta);
//Keep this for our records
VectorCopy(vr.hmdorientation, vr.hmdorientation_last);
// View yaw delta
const float clientview_yaw = vr.clientviewangles[YAW] - vr.hmdorientation[YAW];
vr.clientview_yaw_delta = vr.clientview_yaw_last - clientview_yaw;
vr.clientview_yaw_last = clientview_yaw;
}
//#endif

View file

@ -1,10 +1,18 @@
#ifndef __VR_INPUT_H
#define __VR_INPUT_H
#include "../qcommon/q_shared.h"
#include "vr_types.h"
#if __ANDROID__
void IN_VRInputFrame( void );
void IN_VRInit( void );
void IN_VRSyncActions( void );
void IN_VRUpdateHMD( XrPosef xfStageFromHead );
void IN_VRUpdateControllers( XrPosef xfStageFromHead, float predictedDisplayTime );
void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out);
#endif

View file

@ -4,22 +4,11 @@
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../client/client.h"
#include "../VrApi/Include/VrApi_Types.h"
#include "vr_clientinfo.h"
#include "vr_input.h"
#include "vr_types.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#include <VrApi.h>
#include <VrApi_Helpers.h>
#pragma clang diagnostic pop
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -33,12 +22,30 @@
#define DEFAULT_SUPER_SAMPLING 1.1f
extern vr_clientinfo_t vr;
extern cvar_t *vr_heightAdjust;
int menuResWidth = 0.0f;
int menuResHeight = 0.0f;
int gameResWidth = 0.0f;
int gameResHeight = 0.0f;
XrView* projections;
GLboolean stageSupported = GL_FALSE;
qboolean fullscreenMode = qfalse;
qboolean needRecenter = qtrue;
void VR_UpdateStageBounds(ovrApp* pappState) {
XrExtent2Df stageBounds = {};
XrResult result;
OXR(result = xrGetReferenceSpaceBoundsRect(
pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds));
if (result != XR_SUCCESS) {
ALOGV("Stage bounds query failed: using small defaults");
stageBounds.width = 1.0f;
stageBounds.height = 1.0f;
pappState->CurrentSpace = pappState->FakeStageSpace;
}
ALOGV("Stage bounds: width = %f, depth %f", stageBounds.width, stageBounds.height);
}
void APIENTRY VR_GLDebugLog(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
@ -72,6 +79,8 @@ void APIENTRY VR_GLDebugLog(GLenum source, GLenum type, GLuint id,
void VR_GetResolution(engine_t* engine, int *pWidth, int *pHeight)
{
static int width = 0;
static int height = 0;
float superSampling = 0.0f;
float configuredSuperSampling = Cvar_VariableValue("vr_superSampling");
@ -90,30 +99,135 @@ void VR_GetResolution(engine_t* engine, int *pWidth, int *pHeight)
if (engine)
{
menuResWidth = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH) * DEFAULT_SUPER_SAMPLING;
menuResHeight = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT) * DEFAULT_SUPER_SAMPLING;
// Enumerate the viewport configurations.
uint32_t viewportConfigTypeCount = 0;
OXR(xrEnumerateViewConfigurations(
engine->appState.Instance, engine->appState.SystemId, 0, &viewportConfigTypeCount, NULL));
*pWidth = gameResWidth = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH) * superSampling;
*pHeight = gameResHeight = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT) * superSampling;
XrViewConfigurationType* viewportConfigurationTypes =
(XrViewConfigurationType*)malloc(viewportConfigTypeCount * sizeof(XrViewConfigurationType));
vr.fov_x = vrapi_GetSystemPropertyInt( &engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X);
vr.fov_y = vrapi_GetSystemPropertyInt( &engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y);
OXR(xrEnumerateViewConfigurations(
engine->appState.Instance,
engine->appState.SystemId,
viewportConfigTypeCount,
&viewportConfigTypeCount,
viewportConfigurationTypes));
ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount);
for (uint32_t i = 0; i < viewportConfigTypeCount; i++) {
const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i];
ALOGV(
"Viewport configuration type %d : %s",
viewportConfigType,
viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO ? "Selected" : "");
XrViewConfigurationProperties viewportConfig;
viewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES;
OXR(xrGetViewConfigurationProperties(
engine->appState.Instance, engine->appState.SystemId, viewportConfigType, &viewportConfig));
ALOGV(
"FovMutable=%s ConfigurationType %d",
viewportConfig.fovMutable ? "true" : "false",
viewportConfig.viewConfigurationType);
uint32_t viewCount;
OXR(xrEnumerateViewConfigurationViews(
engine->appState.Instance, engine->appState.SystemId, viewportConfigType, 0, &viewCount, NULL));
if (viewCount > 0) {
XrViewConfigurationView* elements =
(XrViewConfigurationView*)malloc(viewCount * sizeof(XrViewConfigurationView));
for (uint32_t e = 0; e < viewCount; e++) {
elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
elements[e].next = NULL;
}
OXR(xrEnumerateViewConfigurationViews(
engine->appState.Instance,
engine->appState.SystemId,
viewportConfigType,
viewCount,
&viewCount,
elements));
// Cache the view config properties for the selected config type.
if (viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) {
assert(viewCount == ovrMaxNumEyes);
for (uint32_t e = 0; e < viewCount; e++) {
engine->appState.ViewConfigurationView[e] = elements[e];
}
}
free(elements);
} else {
ALOGE("Empty viewport configuration type: %d", viewCount);
}
}
free(viewportConfigurationTypes);
*pWidth = width = engine->appState.ViewConfigurationView[0].recommendedImageRectWidth * superSampling;
*pHeight = height = engine->appState.ViewConfigurationView[0].recommendedImageRectHeight * superSampling;
}
else
{
//use cached values
*pWidth = gameResWidth;
*pHeight = gameResHeight;
*pWidth = width;
*pHeight = height;
}
}
typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(
GLenum target,
GLenum attachment,
GLuint texture,
GLint level,
GLint baseViewIndex,
GLsizei numViews);
void VR_Recenter(engine_t* engine) {
// Calculate recenter reference
XrReferenceSpaceCreateInfo spaceCreateInfo = {};
spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f;
if (engine->appState.CurrentSpace != XR_NULL_HANDLE) {
vec3_t rotation = {0, 0, 0};
XrSpaceLocation loc = {};
loc.type = XR_TYPE_SPACE_LOCATION;
OXR(xrLocateSpace(engine->appState.HeadSpace, engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc));
QuatToYawPitchRoll(loc.pose.orientation, rotation, vr.hmdorientation);
vr.recenterYaw += radians(vr.hmdorientation[YAW]);
spaceCreateInfo.poseInReferenceSpace.orientation.x = 0;
spaceCreateInfo.poseInReferenceSpace.orientation.y = sin(vr.recenterYaw / 2);
spaceCreateInfo.poseInReferenceSpace.orientation.z = 0;
spaceCreateInfo.poseInReferenceSpace.orientation.w = cos(vr.recenterYaw / 2);
}
// Delete previous space instances
if (engine->appState.StageSpace != XR_NULL_HANDLE) {
OXR(xrDestroySpace(engine->appState.StageSpace));
}
if (engine->appState.FakeStageSpace != XR_NULL_HANDLE) {
OXR(xrDestroySpace(engine->appState.FakeStageSpace));
}
// Create a default stage space to use if SPACE_TYPE_STAGE is not
// supported, or calls to xrGetReferenceSpaceBoundsRect fail.
spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
spaceCreateInfo.poseInReferenceSpace.position.y = -1.6750f;
OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.FakeStageSpace));
ALOGV("Created fake stage space from local space with offset");
engine->appState.CurrentSpace = engine->appState.FakeStageSpace;
if (stageSupported) {
spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f;
OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.StageSpace));
ALOGV("Created stage space");
engine->appState.CurrentSpace = engine->appState.StageSpace;
}
// Update menu orientation
vr.menuYaw = 0;
}
void VR_InitRenderer( engine_t* engine ) {
#if ENABLE_GL_DEBUG
@ -121,309 +235,335 @@ void VR_InitRenderer( engine_t* engine ) {
glDebugMessageCallback(VR_GLDebugLog, 0);
#endif
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR =
(PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress(
"glFramebufferTextureMultiviewOVR");
int eyeW, eyeH;
VR_GetResolution(engine, &eyeW, &eyeH);
//for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye)
{
framebuffer_t* framebuffer = &engine->framebuffers;
framebuffer->colorTexture = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D_ARRAY, GL_RGBA8,
eyeW, eyeH, 1, 3);
framebuffer->swapchainLength = vrapi_GetTextureSwapChainLength(framebuffer->colorTexture);
framebuffer->depthBuffers = (GLuint*)malloc(framebuffer->swapchainLength * sizeof(GLuint));
framebuffer->framebuffers = (GLuint*)malloc(framebuffer->swapchainLength * sizeof(GLuint));
for (int index = 0; index < framebuffer->swapchainLength; ++index) {
GLuint colorTexture;
GLenum framebufferStatus;
// Get the viewport configuration info for the chosen viewport configuration type.
engine->appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES;
colorTexture = vrapi_GetTextureSwapChainHandle(framebuffer->colorTexture, index);
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture);
GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f};
glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, borderColor);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
OXR(xrGetViewConfigurationProperties(
engine->appState.Instance, engine->appState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &engine->appState.ViewportConfig));
glGenTextures(1, &framebuffer->depthBuffers[index]);
glBindTexture(GL_TEXTURE_2D_ARRAY, framebuffer->depthBuffers[index]);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH_COMPONENT24, eyeW, eyeH, 2);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
// Get the supported display refresh rates for the system.
{
PFN_xrEnumerateDisplayRefreshRatesFB pfnxrEnumerateDisplayRefreshRatesFB = NULL;
OXR(xrGetInstanceProcAddr(
engine->appState.Instance,
"xrEnumerateDisplayRefreshRatesFB",
(PFN_xrVoidFunction*)(&pfnxrEnumerateDisplayRefreshRatesFB)));
glGenFramebuffers(1, &framebuffer->framebuffers[index]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->framebuffers[index]);
OXR(pfnxrEnumerateDisplayRefreshRatesFB(
engine->appState.Session, 0, &engine->appState.NumSupportedDisplayRefreshRates, NULL));
glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
framebuffer->depthBuffers[index], 0, 0, 2);
glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
colorTexture, 0, 0, 2);
engine->appState.SupportedDisplayRefreshRates =
(float*)malloc(engine->appState.NumSupportedDisplayRefreshRates * sizeof(float));
OXR(pfnxrEnumerateDisplayRefreshRatesFB(
engine->appState.Session,
engine->appState.NumSupportedDisplayRefreshRates,
&engine->appState.NumSupportedDisplayRefreshRates,
engine->appState.SupportedDisplayRefreshRates));
ALOGV("Supported Refresh Rates:");
for (uint32_t i = 0; i < engine->appState.NumSupportedDisplayRefreshRates; i++) {
ALOGV("%d:%f", i, engine->appState.SupportedDisplayRefreshRates[i]);
}
framebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
assert(framebufferStatus == GL_FRAMEBUFFER_COMPLETE);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
}
OXR(xrGetInstanceProcAddr(
engine->appState.Instance,
"xrGetDisplayRefreshRateFB",
(PFN_xrVoidFunction*)(&engine->appState.pfnGetDisplayRefreshRate)));
float currentDisplayRefreshRate = 0.0f;
OXR(engine->appState.pfnGetDisplayRefreshRate(engine->appState.Session, &currentDisplayRefreshRate));
ALOGV("Current System Display Refresh Rate: %f", currentDisplayRefreshRate);
OXR(xrGetInstanceProcAddr(
engine->appState.Instance,
"xrRequestDisplayRefreshRateFB",
(PFN_xrVoidFunction*)(&engine->appState.pfnRequestDisplayRefreshRate)));
// Test requesting the system default.
OXR(engine->appState.pfnRequestDisplayRefreshRate(engine->appState.Session, 0.0f));
ALOGV("Requesting system default display refresh rate");
}
uint32_t numOutputSpaces = 0;
OXR(xrEnumerateReferenceSpaces(engine->appState.Session, 0, &numOutputSpaces, NULL));
XrReferenceSpaceType* referenceSpaces =
(XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType));
OXR(xrEnumerateReferenceSpaces(
engine->appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces));
for (uint32_t i = 0; i < numOutputSpaces; i++) {
if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) {
stageSupported = qtrue;
break;
}
}
free(referenceSpaces);
if (engine->appState.CurrentSpace == XR_NULL_HANDLE) {
VR_Recenter(engine);
}
projections = (XrView*)(malloc(ovrMaxNumEyes * sizeof(XrView)));
ovrRenderer_Create(
engine->appState.Session,
&engine->appState.Renderer,
eyeW,
eyeH);
}
void VR_DestroyRenderer( engine_t* engine ) {
//for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye)
{
if (engine->framebuffers.swapchainLength > 0) {
glDeleteFramebuffers(engine->framebuffers.swapchainLength,
engine->framebuffers.depthBuffers);
free(engine->framebuffers.depthBuffers);
free(engine->framebuffers.framebuffers);
vrapi_DestroyTextureSwapChain(engine->framebuffers.colorTexture);
memset(&engine->framebuffers, 0, sizeof(engine->framebuffers));
}
}
void VR_DestroyRenderer( engine_t* engine )
{
ovrRenderer_Destroy(&engine->appState.Renderer);
free(projections);
}
void VR_ReInitRenderer()
{
VR_DestroyRenderer( VR_GetEngine() );
VR_InitRenderer( VR_GetEngine() );
}
// Assumes landscape cylinder shape.
static ovrMatrix4f CylinderModelMatrix( const int texWidth, const int texHeight,
const ovrVector3f translation,
const float rotateYaw,
const float rotatePitch,
const float radius,
const float density )
void VR_ClearFrameBuffer( int width, int height)
{
const ovrMatrix4f scaleMatrix = ovrMatrix4f_CreateScale( radius, radius * (float)texHeight * VRAPI_PI / density, radius );
const ovrMatrix4f transMatrix = ovrMatrix4f_CreateTranslation( translation.x, translation.y, translation.z );
const ovrMatrix4f rotXMatrix = ovrMatrix4f_CreateRotation( rotateYaw, 0.0f, 0.0f );
const ovrMatrix4f rotYMatrix = ovrMatrix4f_CreateRotation( 0.0f, rotatePitch, 0.0f );
const ovrMatrix4f m0 = ovrMatrix4f_Multiply( &transMatrix, &scaleMatrix );
const ovrMatrix4f m1 = ovrMatrix4f_Multiply( &rotXMatrix, &m0 );
const ovrMatrix4f m2 = ovrMatrix4f_Multiply( &rotYMatrix, &m1 );
return m2;
}
extern cvar_t *vr_screen_dist;
ovrLayerCylinder2 BuildCylinderLayer(engine_t* engine, const int textureWidth, const int textureHeight,
const ovrTracking2 * tracking, float rotatePitch )
{
ovrLayerCylinder2 layer = vrapi_DefaultLayerCylinder2();
const float fadeLevel = 1.0f;
layer.Header.ColorScale.x =
layer.Header.ColorScale.y =
layer.Header.ColorScale.z =
layer.Header.ColorScale.w = fadeLevel;
layer.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA;
layer.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA;
//layer.Header.Flags = VRAPI_FRAME_LAYER_FLAG_CLIP_TO_TEXTURE_RECT;
layer.HeadPose = tracking->HeadPose;
const float density = 4500.0f;
const float rotateYaw = 0.0f;
const float radius = 12.0f;
const float distance = -16.0f;
const ovrVector3f translation = { 0.0f, 1.0f, distance };
ovrMatrix4f cylinderTransform =
CylinderModelMatrix( textureWidth, textureHeight, translation,
rotateYaw, rotatePitch, radius, density );
const float circScale = density * 0.5f / textureWidth;
const float circBias = -circScale * ( 0.5f * ( 1.0f - 1.0f / circScale ) );
for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
{
ovrMatrix4f modelViewMatrix = ovrMatrix4f_Multiply( &tracking->Eye[eye].ViewMatrix, &cylinderTransform );
layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_Inverse( &modelViewMatrix );
layer.Textures[eye].ColorSwapChain = engine->framebuffers.colorTexture;
layer.Textures[eye].SwapChainIndex = engine->framebuffers.swapchainIndex;
// Texcoord scale and bias is just a representation of the aspect ratio. The positioning
// of the cylinder is handled entirely by the TexCoordsFromTanAngles matrix.
const float texScaleX = circScale;
const float texBiasX = circBias;
const float texScaleY = -0.5f;
const float texBiasY = texScaleY * ( 0.5f * ( 1.0f - ( 1.0f / texScaleY ) ) );
layer.Textures[eye].TextureMatrix.M[0][0] = texScaleX;
layer.Textures[eye].TextureMatrix.M[0][2] = texBiasX;
layer.Textures[eye].TextureMatrix.M[1][1] = texScaleY;
layer.Textures[eye].TextureMatrix.M[1][2] = -texBiasY;
layer.Textures[eye].TextureRect.width = 1.0f;
layer.Textures[eye].TextureRect.height = 1.0f;
}
return layer;
}
void VR_ClearFrameBuffer( GLuint frameBuffer, int width, int height)
{
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, frameBuffer );
glEnable( GL_SCISSOR_TEST );
glViewport( 0, 0, width, height );
if (Cvar_VariableIntegerValue("vr_thirdPersonSpectator"))
{
//Blood red.. ish
glClearColor( 0.12f, 0.0f, 0.05f, 1.0f );
}
else
{
//Black
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
}
if (Cvar_VariableIntegerValue("vr_thirdPersonSpectator"))
{
//Blood red.. ish
glClearColor( 0.12f, 0.0f, 0.05f, 1.0f );
}
else
{
//Black
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
}
glScissor( 0, 0, width, height );
glClear( GL_COLOR_BUFFER_BIT );
glScissor( 0, 0, 0, 0 );
glDisable( GL_SCISSOR_TEST );
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
}
void VR_DrawFrame( engine_t* engine ) {
if (!engine->ovr)
{
return;
}
++engine->frameIndex;
engine->predictedDisplayTime = vrapi_GetPredictedDisplayTime(engine->ovr, engine->frameIndex);
engine->tracking = vrapi_GetPredictedTracking2(engine->ovr, engine->predictedDisplayTime);
float fov_y = vrapi_GetSystemPropertyInt( engine->ovr, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y);
float fov_x = vrapi_GetSystemPropertyInt( engine->ovr, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X);
if (vr.weapon_zoomed) {
vr.weapon_zoomLevel += 0.05;
if (vr.weapon_zoomLevel > 2.5f)
vr.weapon_zoomLevel = 2.5f;
vr.weapon_zoomLevel = 2.5f;
}
else {
//Zoom back out quicker
vr.weapon_zoomLevel -= 0.25f;
//Zoom back out quicker
vr.weapon_zoomLevel -= 0.25f;
if (vr.weapon_zoomLevel < 1.0f)
vr.weapon_zoomLevel = 1.0f;
vr.weapon_zoomLevel = 1.0f;
}
const ovrMatrix4f projectionMatrix = ovrMatrix4f_CreateProjectionFov(
fov_x / vr.weapon_zoomLevel, fov_y / vr.weapon_zoomLevel, 0.0f, 0.0f, 1.0f, 0.0f );
GLboolean stageBoundsDirty = GL_TRUE;
if (ovrApp_HandleXrEvents(&engine->appState)) {
VR_Recenter(engine);
}
if (engine->appState.SessionActive == GL_FALSE) {
return;
}
//Projection used for drawing HUD models etc
const ovrMatrix4f monoVRMatrix = ovrMatrix4f_CreateProjectionFov(
30.0f, 30.0f, 0.0f, 0.0f, 1.0f, 0.0f );
if (stageBoundsDirty) {
VR_UpdateStageBounds(&engine->appState);
stageBoundsDirty = GL_FALSE;
}
if (VR_useScreenLayer() ||
(cl.snap.ps.pm_flags & PMF_FOLLOW && vr.follow_mode == VRFM_FIRSTPERSON))
{
fullscreenMode = qtrue;
// NOTE: OpenXR does not use the concept of frame indices. Instead,
// XrWaitFrame returns the predicted display time.
XrFrameWaitInfo waitFrameInfo = {};
waitFrameInfo.type = XR_TYPE_FRAME_WAIT_INFO;
waitFrameInfo.next = NULL;
static ovrLayer_Union2 cylinderLayer;
memset( &cylinderLayer, 0, sizeof( ovrLayer_Union2 ) );
XrFrameState frameState = {};
frameState.type = XR_TYPE_FRAME_STATE;
frameState.next = NULL;
// Add a simple cylindrical layer
cylinderLayer.Cylinder =
BuildCylinderLayer(engine, menuResWidth, menuResWidth * 0.75f, &engine->tracking, radians(vr.menuYaw) );
OXR(xrWaitFrame(engine->appState.Session, &waitFrameInfo, &frameState));
engine->predictedDisplayTime = frameState.predictedDisplayTime;
if (!frameState.shouldRender) {
return;
}
const ovrLayerHeader2* layers[] = {
&cylinderLayer.Header
};
// Get the HMD pose, predicted for the middle of the time period during which
// the new eye images will be displayed. The number of frames predicted ahead
// depends on the pipeline depth of the engine and the synthesis rate.
// The better the prediction, the less black will be pulled in at the edges.
XrFrameBeginInfo beginFrameDesc = {};
beginFrameDesc.type = XR_TYPE_FRAME_BEGIN_INFO;
beginFrameDesc.next = NULL;
OXR(xrBeginFrame(engine->appState.Session, &beginFrameDesc));
// Set up the description for this frame.
ovrSubmitFrameDescription2 frameDesc = { 0 };
frameDesc.Flags = 0;
frameDesc.SwapInterval = 1;
frameDesc.FrameIndex = engine->frameIndex;
frameDesc.DisplayTime = engine->predictedDisplayTime;
frameDesc.LayerCount = 1;
frameDesc.Layers = layers;
XrViewLocateInfo projectionInfo = {};
projectionInfo.type = XR_TYPE_VIEW_LOCATE_INFO;
projectionInfo.viewConfigurationType = engine->appState.ViewportConfig.viewConfigurationType;
projectionInfo.displayTime = frameState.predictedDisplayTime;
projectionInfo.space = engine->appState.CurrentSpace;
re.SetVRHeadsetParms(&projectionMatrix, &monoVRMatrix,
engine->framebuffers.framebuffers[engine->framebuffers.swapchainIndex]);
XrViewState viewState = {XR_TYPE_VIEW_STATE, NULL};
VR_ClearFrameBuffer(engine->framebuffers.framebuffers[engine->framebuffers.swapchainIndex], menuResWidth, menuResHeight);
uint32_t projectionCapacityInput = ovrMaxNumEyes;
uint32_t projectionCountOutput = projectionCapacityInput;
Com_Frame();
OXR(xrLocateViews(
engine->appState.Session,
&projectionInfo,
&viewState,
projectionCapacityInput,
&projectionCountOutput,
projections));
//
// Clear the alpha channel, other way VR API would not transfer the framebuffer fully
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
XrFovf fov = {};
XrPosef invViewTransform[2];
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
invViewTransform[eye] = projections[eye].pose;
engine->framebuffers.swapchainIndex = (engine->framebuffers.swapchainIndex + 1) %
engine->framebuffers.swapchainLength;
fov.angleLeft += projections[eye].fov.angleLeft / 2.0f;
fov.angleRight += projections[eye].fov.angleRight / 2.0f;
fov.angleUp += projections[eye].fov.angleUp / 2.0f;
fov.angleDown += projections[eye].fov.angleDown / 2.0f;
}
vr.fov_x = (fabs(fov.angleLeft) + fabs(fov.angleRight)) * 180.0f / M_PI;
vr.fov_y = (fabs(fov.angleUp) + fabs(fov.angleDown)) * 180.0f / M_PI;
// Hand over the eye images to the time warp.
vrapi_SubmitFrame2(engine->ovr, &frameDesc);
}
else
{
if (fullscreenMode) {
VR_ReInitRenderer();
fullscreenMode = qfalse;
// Update HMD and controllers
IN_VRUpdateHMD( invViewTransform[0] );
IN_VRUpdateControllers( invViewTransform[0], frameState.predictedDisplayTime );
IN_VRSyncActions();
//Projection used for drawing HUD models etc
float hudScale = M_PI * 15.0f / 180.0f;
const ovrMatrix4f monoVRMatrix = ovrMatrix4f_CreateProjectionFov(
-hudScale, hudScale, hudScale, -hudScale, 1.0f, 0.0f );
const ovrMatrix4f projectionMatrix = ovrMatrix4f_CreateProjectionFov(
fov.angleLeft / vr.weapon_zoomLevel,
fov.angleRight / vr.weapon_zoomLevel,
fov.angleUp / vr.weapon_zoomLevel,
fov.angleDown / vr.weapon_zoomLevel,
1.0f, 0.0f );
engine->appState.LayerCount = 0;
memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount);
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer;
int swapchainIndex = engine->appState.Renderer.FrameBuffer.TextureSwapChainIndex;
int glFramebuffer = engine->appState.Renderer.FrameBuffer.FrameBuffers[swapchainIndex];
re.SetVRHeadsetParms(projectionMatrix.M, monoVRMatrix.M, glFramebuffer);
ovrFramebuffer_Acquire(frameBuffer);
ovrFramebuffer_SetCurrent(frameBuffer);
VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height);
Com_Frame();
// Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
ovrFramebuffer_Resolve(frameBuffer);
ovrFramebuffer_Release(frameBuffer);
ovrFramebuffer_SetNone();
XrCompositionLayerProjectionView projection_layer_elements[2] = {};
if (!VR_useScreenLayer() && !(cl.snap.ps.pm_flags & PMF_FOLLOW && vr.follow_mode == VRFM_FIRSTPERSON)) {
vr.menuYaw = vr.hmdorientation[YAW];
if (fullscreenMode) {
VR_ReInitRenderer();
fullscreenMode = qfalse;
}
vr.menuYaw = vr.hmdorientation[YAW];
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer;
ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2();
layer.HeadPose = engine->tracking.HeadPose;
memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView));
projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
projection_layer_elements[eye].pose = invViewTransform[eye];
projection_layer_elements[eye].fov = fov;
const ovrMatrix4f defaultProjection = ovrMatrix4f_CreateProjectionFov(
fov_x, fov_y, 0.0f, 0.0f, 1.0f, 0.0f );
memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage));
projection_layer_elements[eye].subImage.swapchain = frameBuffer->ColorSwapChain.Handle;
projection_layer_elements[eye].subImage.imageRect.offset.x = 0;
projection_layer_elements[eye].subImage.imageRect.offset.y = 0;
projection_layer_elements[eye].subImage.imageRect.extent.width = frameBuffer->ColorSwapChain.Width;
projection_layer_elements[eye].subImage.imageRect.extent.height = frameBuffer->ColorSwapChain.Height;
projection_layer_elements[eye].subImage.imageArrayIndex = eye;
}
XrCompositionLayerProjection projection_layer = {};
projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
projection_layer.space = engine->appState.CurrentSpace;
projection_layer.viewCount = ovrMaxNumEyes;
projection_layer.views = projection_layer_elements;
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
layer.Textures[eye].ColorSwapChain = engine->framebuffers.colorTexture;
layer.Textures[eye].SwapChainIndex = engine->framebuffers.swapchainIndex;
layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection(&defaultProjection);
}
layer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_CHROMATIC_ABERRATION_CORRECTION;
engine->appState.Layers[engine->appState.LayerCount++].Projection = projection_layer;
} else {
VR_ClearFrameBuffer(engine->framebuffers.framebuffers[engine->framebuffers.swapchainIndex], gameResWidth, gameResHeight);
fullscreenMode = qtrue;
re.SetVRHeadsetParms(&projectionMatrix, &monoVRMatrix,
engine->framebuffers.framebuffers[engine->framebuffers.swapchainIndex]);
// Build the cylinder layer
int width = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Width;
int height = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Height;
XrCompositionLayerCylinderKHR cylinder_layer = {};
cylinder_layer.type = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR;
cylinder_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
cylinder_layer.space = engine->appState.CurrentSpace;
cylinder_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
memset(&cylinder_layer.subImage, 0, sizeof(XrSwapchainSubImage));
cylinder_layer.subImage.swapchain = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Handle;
cylinder_layer.subImage.imageRect.offset.x = 0;
cylinder_layer.subImage.imageRect.offset.y = 0;
cylinder_layer.subImage.imageRect.extent.width = width;
cylinder_layer.subImage.imageRect.extent.height = height;
cylinder_layer.subImage.imageArrayIndex = 0;
const XrVector3f axis = {0.0f, 1.0f, 0.0f};
XrVector3f pos = {
invViewTransform[0].position.x - sin(radians(vr.menuYaw)) * 6.0f,
invViewTransform[0].position.y,
invViewTransform[0].position.z - cos(radians(vr.menuYaw)) * 6.0f
};
cylinder_layer.pose.orientation = XrQuaternionf_CreateFromVectorAngle(axis, radians(vr.menuYaw));
cylinder_layer.pose.position = pos;
cylinder_layer.radius = 8.0f;
cylinder_layer.centralAngle = MATH_PI * 0.5f;
cylinder_layer.aspectRatio = width / (float)height / 0.75f;
Com_Frame();
engine->appState.Layers[engine->appState.LayerCount++].Cylinder = cylinder_layer;
}
engine->framebuffers.swapchainIndex = (engine->framebuffers.swapchainIndex + 1) %
engine->framebuffers.swapchainLength;
// Compose the layers for this frame.
const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {};
for (int i = 0; i < engine->appState.LayerCount; i++) {
layers[i] = (const XrCompositionLayerBaseHeader*)&engine->appState.Layers[i];
}
const ovrLayerHeader2* layers[] = {
&layer.Header
};
XrFrameEndInfo endFrameInfo = {};
endFrameInfo.type = XR_TYPE_FRAME_END_INFO;
endFrameInfo.displayTime = frameState.predictedDisplayTime;
endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
endFrameInfo.layerCount = engine->appState.LayerCount;
endFrameInfo.layers = layers;
ovrSubmitFrameDescription2 frameDesc = { 0 };
frameDesc.Flags = 0;
frameDesc.SwapInterval = 1;
frameDesc.FrameIndex = engine->frameIndex;
frameDesc.DisplayTime = engine->predictedDisplayTime;
frameDesc.LayerCount = 1;
frameDesc.Layers = layers;
vrapi_SubmitFrame2(engine->ovr, &frameDesc);
}
OXR(xrEndFrame(engine->appState.Session, &endFrameInfo));
frameBuffer->TextureSwapChainIndex++;
frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength;
if (needRecenter)
{
VR_Recenter(engine);
needRecenter = qfalse;
}
}

View file

@ -9,6 +9,7 @@ void VR_GetResolution( engine_t* engine, int *pWidth, int *pHeight );
void VR_InitRenderer( engine_t* engine );
void VR_DestroyRenderer( engine_t* engine );
void VR_DrawFrame( engine_t* engine );
void VR_ReInitRenderer();
#endif

View file

@ -0,0 +1,594 @@
#include "vr_types.h"
/************************************************************************************
Original file name : XrCompositor_NativeActivity.c
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
*************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <assert.h>
typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(
GLenum target,
GLenum attachment,
GLuint texture,
GLint level,
GLint baseViewIndex,
GLsizei numViews);
/*
================================================================================
ovrFramebuffer
================================================================================
*/
void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) {
frameBuffer->Width = 0;
frameBuffer->Height = 0;
frameBuffer->TextureSwapChainLength = 0;
frameBuffer->TextureSwapChainIndex = 0;
frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE;
frameBuffer->ColorSwapChain.Width = 0;
frameBuffer->ColorSwapChain.Height = 0;
frameBuffer->ColorSwapChainImage = NULL;
frameBuffer->DepthSwapChain.Handle = XR_NULL_HANDLE;
frameBuffer->DepthSwapChain.Width = 0;
frameBuffer->DepthSwapChain.Height = 0;
frameBuffer->DepthSwapChainImage = NULL;
frameBuffer->FrameBuffers = NULL;
}
bool ovrFramebuffer_Create(
XrSession session,
ovrFramebuffer* frameBuffer,
const int width,
const int height) {
frameBuffer->Width = width;
frameBuffer->Height = height;
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR =
(PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress(
"glFramebufferTextureMultiviewOVR");
XrSwapchainCreateInfo swapChainCreateInfo;
memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo));
swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
swapChainCreateInfo.sampleCount = 1;
swapChainCreateInfo.width = width;
swapChainCreateInfo.height = height;
swapChainCreateInfo.faceCount = 1;
swapChainCreateInfo.arraySize = 2;
swapChainCreateInfo.mipCount = 1;
frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width;
frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height;
frameBuffer->DepthSwapChain.Width = swapChainCreateInfo.width;
frameBuffer->DepthSwapChain.Height = swapChainCreateInfo.height;
// Create the color swapchain.
swapChainCreateInfo.format = GL_RGBA8;
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle));
// Create the depth swapchain.
swapChainCreateInfo.format = GL_DEPTH24_STENCIL8;
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->DepthSwapChain.Handle));
// Get the number of swapchain images.
OXR(xrEnumerateSwapchainImages(
frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL));
// Allocate the swapchain images array.
frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(
frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
frameBuffer->DepthSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(
frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
// Populate the swapchain image array.
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
frameBuffer->ColorSwapChainImage[i].next = NULL;
frameBuffer->DepthSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
frameBuffer->DepthSwapChainImage[i].next = NULL;
}
OXR(xrEnumerateSwapchainImages(
frameBuffer->ColorSwapChain.Handle,
frameBuffer->TextureSwapChainLength,
&frameBuffer->TextureSwapChainLength,
(XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage));
OXR(xrEnumerateSwapchainImages(
frameBuffer->DepthSwapChain.Handle,
frameBuffer->TextureSwapChainLength,
&frameBuffer->TextureSwapChainLength,
(XrSwapchainImageBaseHeader*)frameBuffer->DepthSwapChainImage));
frameBuffer->FrameBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
// Create the color buffer texture.
const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image;
const GLuint depthTexture = frameBuffer->DepthSwapChainImage[i].image;
// Create the frame buffer.
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexture, 0, 0, 2));
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 0, 2));
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0, 0, 2));
GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Incomplete frame buffer object: %d", renderFramebufferStatus);
return false;
}
}
return true;
}
void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) {
GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers));
OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle));
OXR(xrDestroySwapchain(frameBuffer->DepthSwapChain.Handle));
free(frameBuffer->ColorSwapChainImage);
free(frameBuffer->DepthSwapChainImage);
free(frameBuffer->FrameBuffers);
ovrFramebuffer_Clear(frameBuffer);
}
void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) {
GL(glBindFramebuffer(
GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]));
}
void ovrFramebuffer_SetNone() {
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
}
void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) {
// Discard the depth buffer, so the tiler won't need to write it back out to memory.
const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT};
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment);
}
void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) {
// Acquire the swapchain image
XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL};
OXR(xrAcquireSwapchainImage(
frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex));
XrSwapchainImageWaitInfo waitInfo;
waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
waitInfo.next = NULL;
waitInfo.timeout = 1000; /* timeout in nanoseconds */
XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo);
int i = 0;
while (res != XR_SUCCESS) {
res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo);
i++;
ALOGV(
" Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f micro seconds)",
i,
waitInfo.timeout * (1E-9));
}
}
void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) {
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL};
OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo));
}
/*
================================================================================
ovrRenderer
================================================================================
*/
void ovrRenderer_Clear(ovrRenderer* renderer) {
ovrFramebuffer_Clear(&renderer->FrameBuffer);
}
void ovrRenderer_Create(
XrSession session,
ovrRenderer* renderer,
int suggestedEyeTextureWidth,
int suggestedEyeTextureHeight) {
// Create the frame buffers.
ovrFramebuffer_Create(
session,
&renderer->FrameBuffer,
suggestedEyeTextureWidth,
suggestedEyeTextureHeight);
}
void ovrRenderer_Destroy(ovrRenderer* renderer) {
ovrFramebuffer_Destroy(&renderer->FrameBuffer);
}
/*
================================================================================
ovrApp
================================================================================
*/
void ovrApp_Clear(ovrApp* app) {
app->Focused = false;
app->Instance = XR_NULL_HANDLE;
app->Session = XR_NULL_HANDLE;
memset(&app->ViewportConfig, 0, sizeof(XrViewConfigurationProperties));
memset(&app->ViewConfigurationView, 0, ovrMaxNumEyes * sizeof(XrViewConfigurationView));
app->SystemId = XR_NULL_SYSTEM_ID;
app->HeadSpace = XR_NULL_HANDLE;
app->StageSpace = XR_NULL_HANDLE;
app->FakeStageSpace = XR_NULL_HANDLE;
app->CurrentSpace = XR_NULL_HANDLE;
app->SessionActive = false;
app->SupportedDisplayRefreshRates = NULL;
app->RequestedDisplayRefreshRateIndex = 0;
app->NumSupportedDisplayRefreshRates = 0;
app->pfnGetDisplayRefreshRate = NULL;
app->pfnRequestDisplayRefreshRate = NULL;
app->SwapInterval = 1;
memset(app->Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount);
app->LayerCount = 0;
app->MainThreadTid = 0;
app->RenderThreadTid = 0;
ovrRenderer_Clear(&app->Renderer);
}
void ovrApp_Destroy(ovrApp* app) {
if (app->SupportedDisplayRefreshRates != NULL) {
free(app->SupportedDisplayRefreshRates);
}
ovrApp_Clear(app);
}
void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) {
if (state == XR_SESSION_STATE_READY) {
assert(app->SessionActive == false);
XrSessionBeginInfo sessionBeginInfo;
memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo));
sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO;
sessionBeginInfo.next = NULL;
sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType;
XrResult result;
OXR(result = xrBeginSession(app->Session, &sessionBeginInfo));
app->SessionActive = (result == XR_SUCCESS);
// Set session state once we have entered VR mode and have a valid session object.
if (app->SessionActive) {
XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT;
XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT;
PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL;
OXR(xrGetInstanceProcAddr(
app->Instance,
"xrPerfSettingsSetPerformanceLevelEXT",
(PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT)));
OXR(pfnPerfSettingsSetPerformanceLevelEXT(
app->Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel));
OXR(pfnPerfSettingsSetPerformanceLevelEXT(
app->Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel));
PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL;
OXR(xrGetInstanceProcAddr(
app->Instance,
"xrSetAndroidApplicationThreadKHR",
(PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR)));
OXR(pfnSetAndroidApplicationThreadKHR(
app->Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, app->MainThreadTid));
OXR(pfnSetAndroidApplicationThreadKHR(
app->Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, app->RenderThreadTid));
}
} else if (state == XR_SESSION_STATE_STOPPING) {
assert(app->SessionActive);
OXR(xrEndSession(app->Session));
app->SessionActive = false;
}
}
GLboolean ovrApp_HandleXrEvents(ovrApp* app) {
XrEventDataBuffer eventDataBuffer = {};
GLboolean recenter = GL_FALSE;
// Poll for events
for (;;) {
XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer);
baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER;
baseEventHeader->next = NULL;
XrResult r;
OXR(r = xrPollEvent(app->Instance, &eventDataBuffer));
if (r != XR_SUCCESS) {
break;
}
switch (baseEventHeader->type) {
case XR_TYPE_EVENT_DATA_EVENTS_LOST:
ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event");
break;
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
const XrEventDataInstanceLossPending* instance_loss_pending_event =
(XrEventDataInstanceLossPending*)(baseEventHeader);
ALOGV(
"xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event: time %f",
FromXrTime(instance_loss_pending_event->lossTime));
} break;
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event");
break;
case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: {
const XrEventDataPerfSettingsEXT* perf_settings_event =
(XrEventDataPerfSettingsEXT*)(baseEventHeader);
ALOGV(
"xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d",
perf_settings_event->type,
perf_settings_event->subDomain,
perf_settings_event->fromLevel,
perf_settings_event->toLevel);
} break;
case XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB: {
const XrEventDataDisplayRefreshRateChangedFB* refresh_rate_changed_event =
(XrEventDataDisplayRefreshRateChangedFB*)(baseEventHeader);
ALOGV(
"xrPollEvent: received XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB event: fromRate %f -> toRate %f",
refresh_rate_changed_event->fromDisplayRefreshRate,
refresh_rate_changed_event->toDisplayRefreshRate);
} break;
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: {
XrEventDataReferenceSpaceChangePending* ref_space_change_event =
(XrEventDataReferenceSpaceChangePending*)(baseEventHeader);
ALOGV(
"xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event: changed space: %d for session %p at time %f",
ref_space_change_event->referenceSpaceType,
(void*)ref_space_change_event->session,
FromXrTime(ref_space_change_event->changeTime));
recenter = GL_TRUE;
} break;
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
const XrEventDataSessionStateChanged* session_state_changed_event =
(XrEventDataSessionStateChanged*)(baseEventHeader);
ALOGV(
"xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f",
session_state_changed_event->state,
(void*)session_state_changed_event->session,
FromXrTime(session_state_changed_event->time));
switch (session_state_changed_event->state) {
case XR_SESSION_STATE_FOCUSED:
app->Focused = true;
break;
case XR_SESSION_STATE_VISIBLE:
app->Focused = false;
break;
case XR_SESSION_STATE_READY:
case XR_SESSION_STATE_STOPPING:
ovrApp_HandleSessionStateChanges(app, session_state_changed_event->state);
break;
default:
break;
}
} break;
default:
ALOGV("xrPollEvent: Unknown event");
break;
}
}
return recenter;
}
/*
================================================================================
ovrMatrix4f
================================================================================
*/
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
const float angleLeft,
const float angleRight,
const float angleUp,
const float angleDown,
const float nearZ,
const float farZ) {
const float tanAngleLeft = tanf(angleLeft);
const float tanAngleRight = tanf(angleRight);
const float tanAngleDown = tanf(angleDown);
const float tanAngleUp = tanf(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;
ovrMatrix4f result;
if (farZ <= nearZ) {
// place the far plane at infinity
result.M[0][0] = 2 / tanAngleWidth;
result.M[0][1] = 0;
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
result.M[0][3] = 0;
result.M[1][0] = 0;
result.M[1][1] = 2 / tanAngleHeight;
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
result.M[1][3] = 0;
result.M[2][0] = 0;
result.M[2][1] = 0;
result.M[2][2] = -1;
result.M[2][3] = -(nearZ + offsetZ);
result.M[3][0] = 0;
result.M[3][1] = 0;
result.M[3][2] = -1;
result.M[3][3] = 0;
} else {
// normal projection
result.M[0][0] = 2 / tanAngleWidth;
result.M[0][1] = 0;
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
result.M[0][3] = 0;
result.M[1][0] = 0;
result.M[1][1] = 2 / tanAngleHeight;
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
result.M[1][3] = 0;
result.M[2][0] = 0;
result.M[2][1] = 0;
result.M[2][2] = -(farZ + offsetZ) / (farZ - nearZ);
result.M[2][3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
result.M[3][0] = 0;
result.M[3][1] = 0;
result.M[3][2] = -1;
result.M[3][3] = 0;
}
return result;
}
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) {
const float ww = q->w * q->w;
const float xx = q->x * q->x;
const float yy = q->y * q->y;
const float zz = q->z * q->z;
ovrMatrix4f out;
out.M[0][0] = ww + xx - yy - zz;
out.M[0][1] = 2 * (q->x * q->y - q->w * q->z);
out.M[0][2] = 2 * (q->x * q->z + q->w * q->y);
out.M[0][3] = 0;
out.M[1][0] = 2 * (q->x * q->y + q->w * q->z);
out.M[1][1] = ww - xx + yy - zz;
out.M[1][2] = 2 * (q->y * q->z - q->w * q->x);
out.M[1][3] = 0;
out.M[2][0] = 2 * (q->x * q->z - q->w * q->y);
out.M[2][1] = 2 * (q->y * q->z + q->w * q->x);
out.M[2][2] = ww - xx - yy + zz;
out.M[2][3] = 0;
out.M[3][0] = 0;
out.M[3][1] = 0;
out.M[3][2] = 0;
out.M[3][3] = 1;
return out;
}
/// Use left-multiplication to accumulate transformations.
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) {
ovrMatrix4f out;
out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] +
a->M[0][3] * b->M[3][0];
out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] +
a->M[1][3] * b->M[3][0];
out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] +
a->M[2][3] * b->M[3][0];
out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] +
a->M[3][3] * b->M[3][0];
out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] +
a->M[0][3] * b->M[3][1];
out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] +
a->M[1][3] * b->M[3][1];
out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] +
a->M[2][3] * b->M[3][1];
out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] +
a->M[3][3] * b->M[3][1];
out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] +
a->M[0][3] * b->M[3][2];
out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] +
a->M[1][3] * b->M[3][2];
out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] +
a->M[2][3] * b->M[3][2];
out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] +
a->M[3][3] * b->M[3][2];
out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] +
a->M[0][3] * b->M[3][3];
out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] +
a->M[1][3] * b->M[3][3];
out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] +
a->M[2][3] * b->M[3][3];
out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] +
a->M[3][3] * b->M[3][3];
return out;
}
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) {
const float sinX = sinf(radiansX);
const float cosX = cosf(radiansX);
const ovrMatrix4f rotationX = {
{{1, 0, 0, 0}, {0, cosX, -sinX, 0}, {0, sinX, cosX, 0}, {0, 0, 0, 1}}};
const float sinY = sinf(radiansY);
const float cosY = cosf(radiansY);
const ovrMatrix4f rotationY = {
{{cosY, 0, sinY, 0}, {0, 1, 0, 0}, {-sinY, 0, cosY, 0}, {0, 0, 0, 1}}};
const float sinZ = sinf(radiansZ);
const float cosZ = cosf(radiansZ);
const ovrMatrix4f rotationZ = {
{{cosZ, -sinZ, 0, 0}, {sinZ, cosZ, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}};
const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply(&rotationY, &rotationX);
return ovrMatrix4f_Multiply(&rotationZ, &rotationXY);
}
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) {
XrVector4f out;
out.x = a->M[0][0] * v->x + a->M[0][1] * v->y + a->M[0][2] * v->z + a->M[0][3] * v->w;
out.y = a->M[1][0] * v->x + a->M[1][1] * v->y + a->M[1][2] * v->z + a->M[1][3] * v->w;
out.z = a->M[2][0] * v->x + a->M[2][1] * v->y + a->M[2][2] * v->z + a->M[2][3] * v->w;
out.w = a->M[3][0] * v->x + a->M[3][1] * v->y + a->M[3][2] * v->z + a->M[3][3] * v->w;
return out;
}
/*
================================================================================
ovrTrackedController
================================================================================
*/
void ovrTrackedController_Clear(ovrTrackedController* controller) {
controller->Active = false;
controller->Pose = XrPosef_Identity();
}

View file

@ -9,26 +9,135 @@
# include <SDL_opengles2.h>
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#include <VrApi.h>
#pragma clang diagnostic pop
//OpenXR
#define XR_USE_GRAPHICS_API_OPENGL_ES 1
#define XR_USE_PLATFORM_ANDROID 1
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#include <jni.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_oculus.h>
#include <openxr/openxr_oculus_helpers.h>
#define MATH_PI 3.14159265358979323846f
#define ALOGE(...) printf(__VA_ARGS__)
#define ALOGV(...) printf(__VA_ARGS__)
typedef union {
XrCompositionLayerProjection Projection;
XrCompositionLayerCylinderKHR Cylinder;
} ovrCompositorLayer_Union;
enum { ovrMaxLayerCount = 1 };
enum { ovrMaxNumEyes = 2 };
#define GL(func) func;
#define OXR(func) func;
typedef struct {
int swapchainLength;
int swapchainIndex;
ovrTextureSwapChain* colorTexture;
GLuint* depthBuffers;
GLuint* framebuffers;
} framebuffer_t;
JavaVM* Vm;
jobject ActivityObject;
JNIEnv* Env;
} ovrJava;
typedef struct {
XrSwapchain Handle;
uint32_t Width;
uint32_t Height;
} ovrSwapChain;
typedef struct {
int Width;
int Height;
uint32_t TextureSwapChainLength;
uint32_t TextureSwapChainIndex;
ovrSwapChain ColorSwapChain;
ovrSwapChain DepthSwapChain;
XrSwapchainImageOpenGLESKHR* ColorSwapChainImage;
XrSwapchainImageOpenGLESKHR* DepthSwapChainImage;
GLuint* FrameBuffers;
} ovrFramebuffer;
typedef struct {
ovrFramebuffer FrameBuffer;
} ovrRenderer;
typedef struct {
GLboolean Active;
XrPosef Pose;
} ovrTrackedController;
typedef struct {
GLboolean Focused;
XrInstance Instance;
XrSession Session;
XrViewConfigurationProperties ViewportConfig;
XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes];
XrSystemId SystemId;
XrSpace HeadSpace;
XrSpace StageSpace;
XrSpace FakeStageSpace;
XrSpace CurrentSpace;
GLboolean SessionActive;
float* SupportedDisplayRefreshRates;
uint32_t RequestedDisplayRefreshRateIndex;
uint32_t NumSupportedDisplayRefreshRates;
PFN_xrGetDisplayRefreshRateFB pfnGetDisplayRefreshRate;
PFN_xrRequestDisplayRefreshRateFB pfnRequestDisplayRefreshRate;
int SwapInterval;
// These threads will be marked as performance threads.
int MainThreadTid;
int RenderThreadTid;
ovrCompositorLayer_Union Layers[ovrMaxLayerCount];
int LayerCount;
ovrRenderer Renderer;
ovrTrackedController TrackedController[2];
} ovrApp;
typedef struct {
float M[4][4];
} ovrMatrix4f;
typedef enum ovrButton_ {
ovrButton_A = 0x00000001, // Set for trigger pulled on the Gear VR and Go Controllers
ovrButton_B = 0x00000002,
ovrButton_RThumb = 0x00000004,
ovrButton_RShoulder = 0x00000008,
ovrButton_X = 0x00000100,
ovrButton_Y = 0x00000200,
ovrButton_LThumb = 0x00000400,
ovrButton_LShoulder = 0x00000800,
ovrButton_Up = 0x00010000,
ovrButton_Down = 0x00020000,
ovrButton_Left = 0x00040000,
ovrButton_Right = 0x00080000,
ovrButton_Enter = 0x00100000, //< Set for touchpad click on the Go Controller, menu
// button on Left Quest Controller
ovrButton_Back = 0x00200000, //< Back button on the Go Controller (only set when
// a short press comes up)
ovrButton_GripTrigger = 0x04000000, //< grip trigger engaged
ovrButton_Trigger = 0x20000000, //< Index Trigger engaged
ovrButton_Joystick = 0x80000000, //< Click of the Joystick
ovrButton_EnumSize = 0x7fffffff
} ovrButton;
typedef struct {
uint64_t frameIndex;
ovrMobile* ovr;
ovrApp appState;
ovrJava java;
double predictedDisplayTime;
ovrTracking2 tracking;
framebuffer_t framebuffers;//[VRAPI_FRAME_LAYER_EYE_MAX];
float predictedDisplayTime;
} engine_t;
typedef enum {
@ -47,4 +156,36 @@ typedef enum {
VRFM_QUERY = 99 //Used to query which mode is active
} vrFollowMode_t;
void ovrApp_Clear(ovrApp* app);
void ovrApp_Destroy(ovrApp* app);
GLboolean ovrApp_HandleXrEvents(ovrApp* app);
void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer);
void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer);
void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer);
void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer);
void ovrFramebuffer_SetNone();
void ovrRenderer_Create(
XrSession session,
ovrRenderer* renderer,
int suggestedEyeTextureWidth,
int suggestedEyeTextureHeight);
void ovrRenderer_Destroy(ovrRenderer* renderer);
void ovrTrackedController_Clear(ovrTrackedController* controller);
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b);
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ);
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q);
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
const float fovDegreesX,
const float fovDegreesY,
const float offsetX,
const float offsetY,
const float nearZ,
const float farZ);
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v);
#endif

View file

@ -4,9 +4,6 @@
#include <android/log.h>
#include <VrApi.h>
#include <VrApi_Helpers.h>
#include <client/keycodes.h>
#include <qcommon/q_shared.h>
#include <qcommon/qcommon.h>
@ -27,7 +24,7 @@ extern void CON_LogcatFn( void (*LogcatFn)( const char* message ) );
static JNIEnv* g_Env = NULL;
static JavaVM* g_JavaVM = NULL;
static jobject g_ActivityObject = NULL;
static bool g_HasFocus = true;
static qboolean g_HasFocus = qtrue;
JNIEXPORT void JNICALL Java_com_drbeef_ioq3quest_MainActivity_nativeCreate(JNIEnv* env, jclass cls, jobject thisObject)
{
@ -39,6 +36,14 @@ JNIEXPORT void JNICALL Java_com_drbeef_ioq3quest_MainActivity_nativeFocusChanged
g_HasFocus = focus;
}
JNIEXPORT void JNICALL Java_com_drbeef_ioq3quest_MainActivity_nativeKey(JNIEnv *env, jclass clazz, jint keycode, jint action)
{
if (action == 0)
{
Com_QueueEvent( 0, SE_CHAR, keycode, qtrue, 0, NULL );
}
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
g_JavaVM = vm;
@ -80,23 +85,22 @@ int main(int argc, char* argv[]) {
Com_Init(args);
NET_Init( );
VR_EnterVR(engine, java);
VR_InitRenderer(engine);
VR_EnterVR(engine, java);
bool hasFocus = true;
bool paused = false;
qboolean hasFocus = qtrue;
qboolean paused = qfalse;
while (1) {
if (hasFocus != g_HasFocus) {
hasFocus = g_HasFocus;
if (!hasFocus && VR_isPauseable()) {
Com_QueueEvent( Sys_Milliseconds(), SE_KEY, K_ESCAPE, qtrue, 0, NULL );
//Com_QueueEvent( Sys_Milliseconds(), SE_KEY, K_CONSOLE, qtrue, 0, NULL );
paused = true;
paused = qtrue;
} else if (hasFocus && paused) {
//Com_QueueEvent( Sys_Milliseconds(), SE_KEY, K_CONSOLE, qtrue, 0, NULL );
Com_QueueEvent( Sys_Milliseconds(), SE_KEY, K_ESCAPE, qtrue, 0, NULL );
paused = false;
paused = qfalse;
}
}
@ -125,4 +129,4 @@ int main(int argc, char* argv[]) {
VR_Destroy(engine);
return 0;
}
}

View file

@ -8,6 +8,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
@ -35,6 +36,7 @@ import static android.system.Os.setenv;
public class MainActivity extends SDLActivity // implements KeyEvent.Callback
{
private static final String SUPPORTED_ASCII = "qwertyuiop[]asdfghjkl;'\\<zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:\"|>ZXCVBNM<>?`1234567890-=~!@#$%^&*()_+";
private int permissionCount = 0;
private static final int READ_EXTERNAL_STORAGE_PERMISSION_ID = 1;
private static final int WRITE_EXTERNAL_STORAGE_PERMISSION_ID = 2;
@ -72,6 +74,17 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
super.onDestroy();
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
//ASCII characters directly passed into the engine
if (SUPPORTED_ASCII.indexOf(event.getUnicodeChar()) >= 0) {
nativeKey(event.getUnicodeChar(), event.getAction());
return true;
}
//special keys using SDL
return super.dispatchKeyEvent(event);
}
/**
* Initializes the Activity only if the permission has been granted.
*/
@ -95,8 +108,10 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
if (requestCode == WRITE_EXTERNAL_STORAGE_PERMISSION_ID) {
finish();
System.exit(0);
try {
create();
} catch (Exception e) {
}
}
}
@ -244,6 +259,7 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
public static native void nativeCreate(MainActivity thisObject);
public static native void nativeFocusChanged(boolean focus);
public static native void nativeKey(int keycode, int action);
static {
System.loadLibrary("main");

View file

@ -215,7 +215,7 @@ itemDef {
group grpSystem
type ITEM_TYPE_SLIDER
text "Brightness:"
cvarfloat "r_gamma" 0.05 0.8 1.2
cvarfloat "r_gamma" 0.05 0.6 1.0
rect 0 90 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133

View file

@ -118,7 +118,7 @@ itemDef {
group grpSystem
type ITEM_TYPE_SLIDER
text "Brightness:"
cvarfloat "r_gamma" 0.05 0.8 1.2
cvarfloat "r_gamma" 0.05 0.6 1.0
rect 99 92 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128