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
47 changed files with 2416 additions and 678 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

@ -8,8 +8,8 @@ android {
applicationId = 'com.drbeef.ioq3quest'
minSdkVersion 25
targetSdkVersion 26
versionCode 1
versionName "1.0"
versionCode 58
versionName "1.1.3"
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'

Binary file not shown.

View file

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.drbeef.ioq3quest"
android:installLocation="preferExternal"
android:versionCode="49"
android:versionName="0.31.4">
android:versionCode="58"
android:versionName="1.1.3">
<uses-feature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true" />
<uses-feature android:glEsVersion="0x00030001" />
<!-- <uses-feature android:name="oculus.software.overlay_keyboard" android:required="false"/>-->

View file

@ -1,4 +1,4 @@
set sv_master1 "13.36.227.32:27950"
set vr_master1 "mp.quakevr.com:27950"
//////////////////////////////////////////////////////////////////////////////////////////////////
// //

View file

@ -1,4 +1,4 @@
set sv_master1 "13.36.227.32:27950"
set vr_master1 "mp.quakevr.com:27950"
set vr_weapon_adjustment_1 "1.0,0,12,-13,-36,0,100"
set vr_weapon_adjustment_2 "1.0,-6,6,-3,0,0,0"

Binary file not shown.

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

@ -2824,7 +2824,7 @@ void CG_DrawActive( void ) {
// clear around the rendered view if sized down
CG_TileClear();
if(!vr->weapon_zoomed)
if(!vr->weapon_zoomed && !vr->virtual_screen)
CG_DrawCrosshair3D();
// offset vieworg appropriately if we're doing stereo separation
@ -2892,7 +2892,7 @@ void CG_DrawActive( void ) {
}
//Now draw the HUD shader in the world
if (trap_Cvar_VariableValue("vr_hudDrawStatus") != 2.0f)
if (trap_Cvar_VariableValue("vr_hudDrawStatus") != 2.0f && !vr->weapon_zoomed && !vr->virtual_screen)
{
refEntity_t ent;
trace_t trace;
@ -2955,7 +2955,7 @@ void CG_DrawActive( void ) {
//Now draw the screen 2D stuff
CG_DrawScreen2D();
if (!vr->weapon_zoomed)
if (!vr->weapon_zoomed && !vr->virtual_screen)
{
cg.drawingHUD = qtrue;

View file

@ -1318,6 +1318,10 @@ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
vec3_t angle;
CG_CalculateVRWeaponPosition(muzzlePoint, angle);
AngleVectors(angle, forward, NULL, NULL );
//Handle this here so it is refreshed on every frame, not just when the lightning gun is first fired
int position = vr->weapon_stabilised ? 4 : (vr->right_handed ? 1 : 2);
trap_HapticEvent("RTCWQuest:fire_tesla", position, 0, 100, 0, 0);
} else {
// !CPMA
AngleVectors( cent->lerpAngles, forward, NULL, NULL );
@ -1724,8 +1728,8 @@ void CG_AddViewWeapon( playerState_t *ps ) {
return;
}
if (vr->weapon_zoomed) {
return; // do not draw weapon model with enabled weapon scope
if (vr->weapon_zoomed || vr->virtual_screen) {
return; // do not draw weapon model with enabled weapon scope or when in menu
}
cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum];
@ -2529,7 +2533,7 @@ void CG_FireWeapon( centity_t *cent ) {
trap_HapticEvent("rocket_fire", position, 0, 100, 0, 0);
break;
case WP_LIGHTNING:
trap_HapticEvent("RTCWQuest:fire_tesla", position, 0, 100, 0, 0);
//Haptics handled in the CG_LightningBolt code
break;
case WP_RAILGUN:
trap_HapticEvent("RTCWQuest:fire_sniper", position, 0, 100, 0, 0);
@ -2543,7 +2547,7 @@ void CG_FireWeapon( centity_t *cent ) {
trap_HapticEvent("bfg_fire", position, 0, 100, 0, 0);
break;
case WP_GRAPPLING_HOOK:
trap_HapticEvent("chainsaw_fire", position, 0, 100, 0, 0);
//No Haptics
break;
#ifdef MISSIONPACK
case WP_NAILGUN:

View file

@ -620,6 +620,10 @@ void CL_FinishMove( usercmd_t *cmd ) {
{
angles[ROLL] = 0; // suppress roll
}
else
{
angles[ROLL] = vr.hmdorientation[ROLL];
}
for (i = 0; i < 3; i++) {
cmd->angles[i] = ANGLE2SHORT(angles[i]);

View file

@ -3622,7 +3622,12 @@ void CL_Init( void ) {
cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60", CVAR_ARCHIVE);
// userinfo
Cvar_Get ("name", "[VR] Player", CVAR_USERINFO | CVAR_ARCHIVE );
char playerName[256];
Com_sprintf(playerName, 256, "[VR] Player#%i", rand() % (99999999 - 10000000) + 10000000);
Cvar_Get ("name", playerName, CVAR_USERINFO | CVAR_ARCHIVE );
if (strcmp(Cvar_VariableString("name"), "[VR] Player") == 0) {
Cvar_Set( "name", playerName);
}
cl_rate = Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE );
Cvar_Get ("snaps", "40", CVAR_USERINFO | CVAR_ARCHIVE );
Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE );
@ -4193,7 +4198,7 @@ void CL_GlobalServers_f( void ) {
int numAddress = 0;
for ( i = 1; i <= MAX_MASTER_SERVERS; i++ ) {
sprintf(command, "sv_master%d", i);
sprintf(command, "vr_master%d", i);
masteraddress = Cvar_VariableString(command);
if(!*masteraddress)
@ -4211,7 +4216,7 @@ void CL_GlobalServers_f( void ) {
return;
}
sprintf(command, "sv_master%d", masterNum);
sprintf(command, "vr_master%d", masterNum);
masteraddress = Cvar_VariableString(command);
if(!*masteraddress)

View file

@ -65,7 +65,7 @@ typedef struct {
menuradiobutton_s autoswitch;
menuradiobutton_s scope;
menuradiobutton_s twohanded;
menulist_s twohanded;
menulist_s directionmode;
menulist_s snapturn;
menuradiobutton_s uturn;
@ -84,7 +84,7 @@ static controls3_t s_controls3;
static void Controls3_SetMenuItems( void ) {
s_controls3.autoswitch.curvalue = trap_Cvar_VariableValue( "cg_autoswitch" ) != 0;
s_controls3.scope.curvalue = trap_Cvar_VariableValue( "vr_weaponScope" ) != 0;
s_controls3.twohanded.curvalue = trap_Cvar_VariableValue( "vr_twoHandedWeapons" ) != 0;
s_controls3.twohanded.curvalue = trap_Cvar_VariableValue( "vr_twoHandedWeapons" );
s_controls3.directionmode.curvalue = (int)trap_Cvar_VariableValue( "vr_directionMode" ) % NUM_DIRECTIONMODE;
s_controls3.snapturn.curvalue = (int)trap_Cvar_VariableValue( "vr_snapturn" ) / 45;
s_controls3.uturn.curvalue = trap_Cvar_VariableValue( "vr_uturn" ) != 0;
@ -213,6 +213,19 @@ static void Controls3_MenuEvent( void* ptr, int notification ) {
break;
case ID_SWITCHTHUMBSTICKS:
{
if (s_controls3.switchthumbsticks.curvalue) {
trap_Cvar_Set("vr_button_map_A", "+button2"); // Use Item
trap_Cvar_Set("vr_button_map_B", "+button3"); // Gesture
trap_Cvar_Set("vr_button_map_X", "+moveup"); // Jump
trap_Cvar_Set("vr_button_map_Y", "+movedown"); // Crouch
} else {
trap_Cvar_Set("vr_button_map_A", "+moveup"); // Jump
trap_Cvar_Set("vr_button_map_B", "+movedown"); // Crouch
trap_Cvar_Set("vr_button_map_X", "+button2"); // Use Item
trap_Cvar_Set("vr_button_map_Y", "+button3"); // Gesture
}
}
trap_Cvar_SetValue( "vr_switchThumbsticks", s_controls3.switchthumbsticks.curvalue );
break;
@ -255,6 +268,14 @@ static void Controls3_MenuInit( void ) {
NULL
};
static const char *s_twohandedmode[] =
{
"Disabled",
"Enabled (Basic)",
"Enabled (VR Gun Stock)",
NULL
};
memset( &s_controls3, 0 ,sizeof(controls3_t) );
Controls3_Cache();
@ -304,13 +325,15 @@ static void Controls3_MenuInit( void ) {
s_controls3.scope.generic.y = y;
y += BIGCHAR_HEIGHT+2;
s_controls3.twohanded.generic.type = MTYPE_RADIOBUTTON;
s_controls3.twohanded.generic.type = MTYPE_SPINCONTROL;
s_controls3.twohanded.generic.name = "Two-Handed Weapons:";
s_controls3.twohanded.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_controls3.twohanded.generic.callback = Controls3_MenuEvent;
s_controls3.twohanded.generic.id = ID_TWOHANDED;
s_controls3.twohanded.generic.x = VR_X_POS;
s_controls3.twohanded.generic.y = y;
s_controls3.twohanded.itemnames = s_twohandedmode;
s_controls3.twohanded.numitems = 3;
y += BIGCHAR_HEIGHT+2;
s_controls3.directionmode.generic.type = MTYPE_SPINCONTROL;

View file

@ -107,8 +107,8 @@ Additional Contributions
Bummser, Skillfur, Ceno, Cukier, Eispfogel, Pizzaluigi
Dedicated Beta Testers
XQuader, Ceno, Cukier, Bummser, Retro1N, Benny91, April, Ikarus,
Bim, Lubos, MasakaPete, Config2, Maniac, Ghostdog72, Slydog43,
f2hunter, XQuader, Ceno, Cukier, Bummser, Retro1N, Benny91, April, Ikarus,
GeTall, Lubos, MasakaPete, Config2, Maniac, Ghostdog72, Slydog43,
Cornelius, Ferret, RealityForge, PvtGenO, SatanSlayer
Special Thanks to the whole discord!
@ -140,16 +140,18 @@ Special Thanks to the whole discord!
y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawProportionalString( 320, y, "Additional Contributions", UI_CENTER|UI_SMALLFONT, color_red );
y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawString( 320, y, "Bummser, Skillfur, Ceno, Cukier, Eispfogel, Pizzaluigi", UI_CENTER|UI_SMALLFONT, color_white );
UI_DrawString( 320, y, "Bummser, Skillfur, Ceno, Cukier, Eispfogel", UI_CENTER|UI_SMALLFONT, color_white );
y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawString( 320, y, "Omarlego (custom Q3Q background), Pizzaluigi", UI_CENTER|UI_SMALLFONT, color_white );
y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawProportionalString( 320, y, "Dedicated Beta Testers", UI_CENTER|UI_SMALLFONT, color_red );
y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawString( 320, y, "XQuader, Ceno, Cukier, Bummser, Retro1N, Benny91, April, Ikarus,", UI_CENTER|UI_SMALLFONT, color_white );
UI_DrawString( 320, y, "f2hunter, XQuader, Ceno, Cukier, Bummser, Retro1N, Benny91, Madmac(Ikarus),", UI_CENTER|UI_SMALLFONT, color_white );
y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawString( 320, y, "Bim, Lubos, MasakaPete, Config2, Maniac, Ghostdog72, Slydog43,", UI_CENTER|UI_SMALLFONT, color_white );
UI_DrawString( 320, y, "GeTall, Lubos, MasakaPete, Config2, Maniac, Ghostdog72, Slydog43,", UI_CENTER|UI_SMALLFONT, color_white );
y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawString( 320, y, "Cornelius, Ferret, RealityForge, PvtGenO, SatanSlayer", UI_CENTER|UI_SMALLFONT, color_white );
UI_DrawString( 320, y, "Myrothas, April, Cornelius, Ferret, RealityForge, PvtGenO, SatanSlayer", UI_CENTER|UI_SMALLFONT, color_white );
y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
UI_DrawProportionalString( 320, y, "Special Thanks to the whole Team Beef discord!", UI_CENTER|UI_SMALLFONT, color_red );
@ -163,7 +165,7 @@ Special Thanks to the whole discord!
UI_DrawProportionalString(320, y, "original ioquake3 contributors!", UI_CENTER | UI_SMALLFONT, color_orange);
}
UI_DrawString( 320, 459, "Quake3Quest: https://www.quake3.quakevr.com/", UI_CENTER|UI_SMALLFONT, color_red );
UI_DrawString( 320, 459, "Quake3Quest: https://quake3.quakevr.com/", UI_CENTER|UI_SMALLFONT, color_red );
}

View file

@ -229,7 +229,7 @@ static void Main_MenuDraw( void ) {
int yPos = 410;
if (uis.demoversion) {
yPos = 372;
UI_DrawString( 320, yPos, "Demo: Play the 1st tier of SP or MP on our demo servers", UI_CENTER|UI_SMALLFONT, color_white );
UI_DrawString( 320, yPos, "DEMO: You can only play on Demo-Servers and play first part of SP!", UI_CENTER|UI_SMALLFONT, color_white );
yPos += SMALLCHAR_HEIGHT;
UI_DrawString( 320, yPos, "Install full game for more!", UI_CENTER|UI_SMALLFONT, color_white );
yPos += SMALLCHAR_HEIGHT;

View file

@ -160,7 +160,7 @@ static char* netnames[] = {
NULL
};
static char quake3questMessage[] = "Visit quake3.quakevr.com - For the Team Beef Discord invite for news/events/chat";
static char quake3questMessage[] = "Meetups: 21h CET (EU) / 9pm EST (USA). stats.quakevr.com for server status.";
const char* punkbuster_items[] = {
"Disabled",
@ -194,6 +194,7 @@ typedef struct servernode_s {
int minPing;
int maxPing;
qboolean bPB;
qboolean demo;
} servernode_t;
@ -395,7 +396,6 @@ static void ArenaServers_UpdatePicture( void ) {
servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname );
g_arenaservers.mappic.generic.name = picname;
}
// force shader update during draw
@ -500,7 +500,17 @@ static void ArenaServers_UpdateMenu( void ) {
return;
}
// build list box strings - apply culling filters
//Specify should always be grayed out
g_arenaservers.specify.generic.flags |= QMF_GRAYED;
servernode_t* servernode;
servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
if( servernode && uis.demoversion && !servernode->demo) {
//Demo pak cannot connect to a non-demo server
g_arenaservers.go.generic.flags |= QMF_GRAYED;
}
// build list box strings - apply culling filters
servernodeptr = g_arenaservers.serverlist;
count = *g_arenaservers.numservers;
for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
@ -562,10 +572,20 @@ static void ArenaServers_UpdateMenu( void ) {
pingColor = S_COLOR_RED;
}
Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %4s%s%3d " S_COLOR_YELLOW "%s",
servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
servernodeptr->maxclients, servernodeptr->gamename,
netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
if (uis.demoversion && !servernodeptr->demo)
{
Com_sprintf( buff, MAX_LISTBOXWIDTH, S_COLOR_MID_GREY "%-20.20s %-12.12s %2d/%2d %-8.8s %4s%s%3d " S_COLOR_YELLOW "%s",
servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
servernodeptr->maxclients, servernodeptr->gamename,
netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
}
else
{
Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %4s%s%3d " S_COLOR_YELLOW "%s",
servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
servernodeptr->maxclients, servernodeptr->gamename,
netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
}
j++;
}
@ -682,6 +702,8 @@ static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
servernodeptr->minPing = atoi( Info_ValueForKey( info, "minPing") );
servernodeptr->maxPing = atoi( Info_ValueForKey( info, "maxPing") );
servernodeptr->bPB = atoi( Info_ValueForKey( info, "punkbuster") );
servernodeptr->demo = (strcasestr(info, "demo") != NULL) ||
(strcmp(servernodeptr->hostname, "Quake3Quest") == 0); // Demo can connect to another quest
/*
s = Info_ValueForKey( info, "nettype" );
@ -1104,7 +1126,7 @@ int ArenaServers_SetType( int type )
if(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5)
{
char masterstr[2], cvarname[sizeof("sv_master1")];
char masterstr[2], cvarname[sizeof("vr_master1")];
int direction;
if (type == g_servertype || type == ((g_servertype+1) % UIAS_NUM_SOURCES)) {
@ -1115,7 +1137,7 @@ int ArenaServers_SetType( int type )
while(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5)
{
Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type - UIAS_GLOBAL0);
Com_sprintf(cvarname, sizeof(cvarname), "vr_master%d", type - UIAS_GLOBAL0);
trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr));
if(*masterstr)
break;
@ -1239,6 +1261,17 @@ static void ArenaServers_Event( void* ptr, int event ) {
case ID_LIST:
if( event == QM_GOTFOCUS ) {
ArenaServers_UpdatePicture();
servernode_t* servernode;
servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
if( servernode && uis.demoversion && !servernode->demo) {
//Demo pak cannot connect to a non-demo server
g_arenaservers.go.generic.flags |= QMF_GRAYED;
}
else
{
g_arenaservers.go.generic.flags &= ~QMF_GRAYED;
}
}
break;
@ -1583,8 +1616,8 @@ static void ArenaServers_MenuInit( void ) {
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.punkbuster );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.pblogo );
// Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.punkbuster );
// Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.pblogo );
ArenaServers_LoadFavorites();
@ -1602,7 +1635,7 @@ static void ArenaServers_MenuInit( void ) {
g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer );
g_arenaservers.showempty.curvalue = g_emptyservers;
g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
// g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
// force to initial state and refresh
g_arenaservers.master.curvalue = g_servertype = ArenaServers_SetType(g_servertype);
@ -1631,7 +1664,7 @@ void ArenaServers_Cache( void ) {
trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP );
trap_R_RegisterShaderNoMip( ART_PUNKBUSTER );
// trap_R_RegisterShaderNoMip( ART_PUNKBUSTER );
}

View file

@ -99,6 +99,14 @@ static const char *gametype_items[] = {
static int gametype_remap[] = {GT_FFA, GT_TEAM, GT_TOURNAMENT, GT_CTF};
static int gametype_remap2[] = {0, 2, 0, 1, 3};
static int fraglimit_values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40, 50, 75, 100, 200, 500, -1};
static const char *fraglimit_items[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "15",
"20", "25", "30", "40", "50", "75", "100", "200", "500", NULL};
static int timelimit_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40, 50, 60, 120, 240, 480, -1};
static const char *timelimit_items[] = {"No limit", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "15",
"20", "25", "30", "40", "50", "60", "120", "240", "480", NULL};
// use ui_servers2.c definition
extern const char* punkbuster_items[];
@ -631,9 +639,9 @@ typedef struct {
menubitmap_s picframe;
menulist_s dedicated;
menufield_s timelimit;
menufield_s fraglimit;
menufield_s flaglimit;
menulist_s timelimit;
menulist_s fraglimit;
menulist_s flaglimit;
menuradiobutton_s friendlyfire;
menufield_s hostname;
menuradiobutton_s pure;
@ -735,9 +743,9 @@ static void ServerOptions_Start( void ) {
char buf[64];
const char *info;
timelimit = atoi( s_serveroptions.timelimit.field.buffer );
fraglimit = atoi( s_serveroptions.fraglimit.field.buffer );
flaglimit = atoi( s_serveroptions.flaglimit.field.buffer );
timelimit = timelimit_values[s_serveroptions.timelimit.curvalue];
fraglimit = fraglimit_values[s_serveroptions.fraglimit.curvalue];
flaglimit = fraglimit_values[s_serveroptions.flaglimit.curvalue];
dedicated = s_serveroptions.dedicated.curvalue;
friendlyfire = s_serveroptions.friendlyfire.curvalue;
pure = s_serveroptions.pure.curvalue;
@ -1127,6 +1135,21 @@ static void ServerOptions_InitBotNames( void ) {
}
}
static int getValueIndex(int* values, int value, int defaultVal)
{
int index = 0;
while (values[index] != -1)
{
if (values[index] == value)
{
return index;
}
index++;
}
//Just return the default
return defaultVal;
}
/*
=================
@ -1141,24 +1164,24 @@ static void ServerOptions_SetMenuItems( void ) {
switch( s_serveroptions.gametype ) {
case GT_FFA:
default:
Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_fraglimit" ) ) );
Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_timelimit" ) ) );
s_serveroptions.fraglimit.curvalue = getValueIndex(fraglimit_values, trap_Cvar_VariableValue( "ui_ffa_fraglimit" ), 11);
s_serveroptions.timelimit.curvalue = getValueIndex(timelimit_values, trap_Cvar_VariableValue( "ui_ffa_timelimit" ), 0);
break;
case GT_TOURNAMENT:
Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_fraglimit" ) ) );
Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_timelimit" ) ) );
s_serveroptions.fraglimit.curvalue = getValueIndex(fraglimit_values, trap_Cvar_VariableValue( "ui_tourney_fraglimit" ), 11);
s_serveroptions.timelimit.curvalue = getValueIndex(timelimit_values, trap_Cvar_VariableValue( "ui_tourney_timelimit" ), 0);
break;
case GT_TEAM:
Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_fraglimit" ) ) );
Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_timelimit" ) ) );
s_serveroptions.fraglimit.curvalue = getValueIndex(fraglimit_values, trap_Cvar_VariableValue( "ui_team_fraglimit"), 11);
s_serveroptions.timelimit.curvalue = getValueIndex(timelimit_values, trap_Cvar_VariableValue( "ui_team_timelimit" ), 0);
s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_team_friendly" ) );
break;
case GT_CTF:
Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_ctf_capturelimit" ) ) );
Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ctf_timelimit" ) ) );
s_serveroptions.flaglimit.curvalue = getValueIndex(fraglimit_values, trap_Cvar_VariableValue( "ui_ctf_capturelimit" ), 11);
s_serveroptions.timelimit.curvalue = getValueIndex(timelimit_values, trap_Cvar_VariableValue( "ui_ctf_timelimit" ), 0);
s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_ctf_friendly" ) );
break;
}
@ -1281,35 +1304,35 @@ static void ServerOptions_MenuInit( qboolean multiplayer ) {
y = 272;
if( s_serveroptions.gametype != GT_CTF ) {
s_serveroptions.fraglimit.generic.type = MTYPE_FIELD;
s_serveroptions.fraglimit.generic.type = MTYPE_SPINCONTROL;
s_serveroptions.fraglimit.generic.name = "Frag Limit:";
s_serveroptions.fraglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_serveroptions.fraglimit.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_serveroptions.fraglimit.generic.x = OPTIONS_X;
s_serveroptions.fraglimit.generic.y = y;
s_serveroptions.fraglimit.generic.statusbar = ServerOptions_StatusBar;
s_serveroptions.fraglimit.field.widthInChars = 3;
s_serveroptions.fraglimit.field.maxchars = 3;
s_serveroptions.fraglimit.curvalue = 11;
s_serveroptions.fraglimit.itemnames = fraglimit_items;
}
else {
s_serveroptions.flaglimit.generic.type = MTYPE_FIELD;
s_serveroptions.flaglimit.generic.type = MTYPE_SPINCONTROL;
s_serveroptions.flaglimit.generic.name = "Capture Limit:";
s_serveroptions.flaglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_serveroptions.flaglimit.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_serveroptions.flaglimit.generic.x = OPTIONS_X;
s_serveroptions.flaglimit.generic.y = y;
s_serveroptions.flaglimit.generic.statusbar = ServerOptions_StatusBar;
s_serveroptions.flaglimit.field.widthInChars = 3;
s_serveroptions.flaglimit.field.maxchars = 3;
s_serveroptions.flaglimit.curvalue = 9;
s_serveroptions.flaglimit.itemnames = fraglimit_items;
}
y += BIGCHAR_HEIGHT+2;
s_serveroptions.timelimit.generic.type = MTYPE_FIELD;
s_serveroptions.timelimit.generic.type = MTYPE_SPINCONTROL;
s_serveroptions.timelimit.generic.name = "Time Limit:";
s_serveroptions.timelimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_serveroptions.timelimit.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_serveroptions.timelimit.generic.x = OPTIONS_X;
s_serveroptions.timelimit.generic.y = y;
s_serveroptions.timelimit.generic.statusbar = ServerOptions_StatusBar;
s_serveroptions.timelimit.field.widthInChars = 3;
s_serveroptions.timelimit.field.maxchars = 3;
s_serveroptions.flaglimit.curvalue = 0;
s_serveroptions.timelimit.itemnames = timelimit_items;
if( s_serveroptions.gametype >= GT_TEAM ) {
y += BIGCHAR_HEIGHT+2;

View file

@ -249,10 +249,12 @@ GRAPHICS OPTIONS MENU
#define ID_PLAYERSHADOW 109
#define ID_GAMMA 110
#define ID_HIGHQUALITYSKY 111
#define ID_SUPERSAMPLING 112
#define NUM_REFRESHRATE 4
#define NUM_REFRESHRATE 5
#define NUM_SHADOWS 3
#define NUM_RAILGUN 2
#define NUM_SUPERSAMPLING 6
typedef struct {
menuframework_s menu;
@ -276,6 +278,7 @@ typedef struct {
menulist_s playershadow;
menuslider_s gamma;
menuradiobutton_s highqualitysky;
menulist_s supersampling;
menubitmap_s apply;
menubitmap_s back;
@ -292,6 +295,7 @@ typedef struct
int playershadow;
float gamma;
int highqualitysky;
float supersampling;
} InitialVideoOptions_s;
static InitialVideoOptions_s s_ivo;
@ -313,6 +317,7 @@ static void GraphicsOptions_GetInitialVideo( void )
s_ivo.playershadow = s_graphicsoptions.playershadow.curvalue;
s_ivo.gamma = s_graphicsoptions.gamma.curvalue;
s_ivo.highqualitysky = s_graphicsoptions.highqualitysky.curvalue;
s_ivo.supersampling = s_graphicsoptions.supersampling.curvalue;
}
/*
@ -401,6 +406,9 @@ static void GraphicsOptions_Event( void* ptr, int event ) {
case 3:
refresh = 90;
break;
case 4:
refresh = 120;
break;
}
trap_Cvar_SetValue("vr_refreshrate", refresh);
}
@ -452,6 +460,32 @@ static void GraphicsOptions_Event( void* ptr, int event ) {
trap_Cvar_SetValue( "r_fastsky", !s_graphicsoptions.highqualitysky.curvalue );
break;
case ID_SUPERSAMPLING: {
float supersampling;
switch (s_graphicsoptions.supersampling.curvalue) {
case 0:
supersampling = 0.8;
break;
case 1:
supersampling = 0.9;
break;
case 2:
supersampling = 1.0;
break;
case 3:
supersampling = 1.1;
break;
case 4:
supersampling = 1.2;
break;
case 5:
supersampling = 1.3;
break;
}
trap_Cvar_SetValue("vr_superSampling", supersampling);
}
break;
case ID_DRIVERINFO:
UI_DriverInfo_Menu();
break;
@ -542,6 +576,12 @@ static void GraphicsOptions_SetMenuItems( void )
case 90:
s_graphicsoptions.refreshrate.curvalue = 3;
break;
case 120:
s_graphicsoptions.refreshrate.curvalue = 4;
break;
default:
s_graphicsoptions.refreshrate.curvalue = 1;
break;
}
switch ( (int) trap_Cvar_VariableValue( "cg_shadows" ) )
@ -570,6 +610,23 @@ static void GraphicsOptions_SetMenuItems( void )
break;
}
float superSampling = trap_Cvar_VariableValue( "vr_superSampling" );
if (superSampling == 0.8f) {
s_graphicsoptions.supersampling.curvalue = 0;
} else if (superSampling == 0.9f) {
s_graphicsoptions.supersampling.curvalue = 1;
} else if (superSampling == 1.0f) {
s_graphicsoptions.supersampling.curvalue = 2;
} else if (superSampling == 1.1f) {
s_graphicsoptions.supersampling.curvalue = 3;
} else if (superSampling == 1.2f) {
s_graphicsoptions.supersampling.curvalue = 4;
} else if (superSampling == 1.3f) {
s_graphicsoptions.supersampling.curvalue = 5;
} else {
s_graphicsoptions.supersampling.curvalue = 3;
}
s_graphicsoptions.lighting.curvalue = trap_Cvar_VariableValue( "r_vertexLight" ) != 0;
s_graphicsoptions.railgun.curvalue = trap_Cvar_VariableValue( "cg_oldRail" );
s_graphicsoptions.gamma.curvalue = trap_Cvar_VariableValue( "r_gamma" );
@ -602,9 +659,10 @@ void GraphicsOptions_MenuInit( void )
static const char *s_refreshrate[] =
{
"60",
"72 (Recommended)",
"72",
"80",
"90",
"120",
NULL
};
@ -623,6 +681,17 @@ void GraphicsOptions_MenuInit( void )
NULL
};
static const char *s_supersampling[] =
{
"0.8",
"0.9",
"1.0",
"1.1",
"1.2",
"1.3",
NULL
};
int y;
// zero set all our globals
@ -687,7 +756,7 @@ void GraphicsOptions_MenuInit( void )
s_graphicsoptions.network.style = UI_RIGHT;
s_graphicsoptions.network.color = color_red;
y = 254 - 5 * (BIGCHAR_HEIGHT + 2);
y = 254 - 6 * (BIGCHAR_HEIGHT + 2);
// references "vr_refreshrate"
s_graphicsoptions.refreshrate.generic.type = MTYPE_SPINCONTROL;
@ -701,6 +770,18 @@ void GraphicsOptions_MenuInit( void )
s_graphicsoptions.refreshrate.numitems = NUM_REFRESHRATE;
y += BIGCHAR_HEIGHT+2;
// references "vr_superSampling"
s_graphicsoptions.supersampling.generic.type = MTYPE_SPINCONTROL;
s_graphicsoptions.supersampling.generic.name = "Supersampling:";
s_graphicsoptions.supersampling.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_graphicsoptions.supersampling.generic.x = 400;
s_graphicsoptions.supersampling.generic.y = y;
s_graphicsoptions.supersampling.itemnames = s_supersampling;
s_graphicsoptions.supersampling.generic.callback = GraphicsOptions_Event;
s_graphicsoptions.supersampling.generic.id = ID_SUPERSAMPLING;
s_graphicsoptions.supersampling.numitems = NUM_SUPERSAMPLING;
y += BIGCHAR_HEIGHT+2;
// references "r_gamma"
s_graphicsoptions.gamma.generic.type = MTYPE_SLIDER;
s_graphicsoptions.gamma.generic.name = "Brightness:";
@ -709,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"
@ -828,6 +909,7 @@ void GraphicsOptions_MenuInit( void )
Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.network );
Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.refreshrate );
Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.supersampling );
Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.railgun );
Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.gamma );
Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.lighting );

View file

@ -47,7 +47,7 @@ vec4_t colorLtGrey = {0.75, 0.75, 0.75, 1};
vec4_t colorMdGrey = {0.5, 0.5, 0.5, 1};
vec4_t colorDkGrey = {0.25, 0.25, 0.25, 1};
vec4_t g_color_table[8] =
vec4_t g_color_table[10] =
{
{0.0, 0.0, 0.0, 1.0},
{1.0, 0.0, 0.0, 1.0},
@ -57,6 +57,8 @@ vec4_t g_color_table[8] =
{0.0, 1.0, 1.0, 1.0},
{1.0, 0.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{0.75, 0.75, 0.75, 1.0},
{0.5, 0.5, 0.5, 1.0},
};

View file

@ -431,7 +431,9 @@ qboolean Q_IsColorString(const char *p); // ^[0-9a-zA-Z]
#define COLOR_CYAN '5'
#define COLOR_MAGENTA '6'
#define COLOR_WHITE '7'
#define ColorIndexForNumber(c) ((c) & 0x07)
#define COLOR_LIGHT_GREY '8'
#define COLOR_MID_GREY '9'
#define ColorIndexForNumber(c) ((c) & 0x0f)
#define ColorIndex(c) (ColorIndexForNumber((c) - '0'))
#define S_COLOR_BLACK "^0"
@ -442,8 +444,10 @@ qboolean Q_IsColorString(const char *p); // ^[0-9a-zA-Z]
#define S_COLOR_CYAN "^5"
#define S_COLOR_MAGENTA "^6"
#define S_COLOR_WHITE "^7"
#define S_COLOR_LIGHT_GREY "^8"
#define S_COLOR_MID_GREY "^9"
extern vec4_t g_color_table[8];
extern vec4_t g_color_table[10];
#define MAKERGB( v, r, g, b ) v[0]=r;v[1]=g;v[2]=b
#define MAKERGBA( v, r, g, b, a ) v[0]=r;v[1]=g;v[2]=b;v[3]=a

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

@ -274,7 +274,7 @@ extern cvar_t *sv_maxclients;
extern cvar_t *sv_privateClients;
extern cvar_t *sv_hostname;
extern cvar_t *sv_master[MAX_MASTER_SERVERS];
extern cvar_t *vr_master[MAX_MASTER_SERVERS];
extern cvar_t *sv_reconnectlimit;
extern cvar_t *sv_showloss;
extern cvar_t *sv_padPackets;

View file

@ -672,11 +672,11 @@ void SV_Init (void)
sv_allowDownload = Cvar_Get ("sv_allowDownload", "1", CVAR_SERVERINFO);
Cvar_Get ("sv_dlURL", "", CVAR_SERVERINFO | CVAR_ARCHIVE);
sv_master[0] = Cvar_Get("sv_master1", "", 0); // This is set to our Q3Q master
sv_master[1] = Cvar_Get("sv_master2", MASTER_SERVER_NAME, 0);
sv_master[2] = Cvar_Get("sv_master3", "master.ioquake3.org", 0);
vr_master[0] = Cvar_Get("vr_master1", "mp.quakevr.com:27950", 0); // This is set to our Q3Q master
vr_master[1] = Cvar_Get("vr_master2", MASTER_SERVER_NAME, 0);
vr_master[2] = Cvar_Get("vr_master3", "master.ioquake3.org", 0);
for(index = 3; index < MAX_MASTER_SERVERS; index++)
sv_master[index] = Cvar_Get(va("sv_master%d", index + 1), "", CVAR_ARCHIVE);
vr_master[index] = Cvar_Get(va("vr_master%d", index + 1), "", CVAR_ARCHIVE);
sv_reconnectlimit = Cvar_Get ("sv_reconnectlimit", "3", 0);
sv_showloss = Cvar_Get ("sv_showloss", "0", 0);

View file

@ -41,7 +41,7 @@ cvar_t *sv_maxclients;
cvar_t *sv_privateClients; // number of clients reserved for password
cvar_t *sv_hostname;
cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address
cvar_t *vr_master[MAX_MASTER_SERVERS]; // master server ip address
cvar_t *sv_reconnectlimit; // minimum seconds between connect messages
cvar_t *sv_showloss; // report when usercmds are lost
cvar_t *sv_padPackets; // add nop bytes to messages
@ -262,20 +262,20 @@ void SV_MasterHeartbeat(const char *message)
// send to group masters
for (i = 0; i < MAX_MASTER_SERVERS; i++)
{
if(!sv_master[i]->string[0])
if(!vr_master[i]->string[0])
continue;
// see if we haven't already resolved the name or if it's been over 24 hours
// resolving usually causes hitches on win95, so only do it when needed
if (sv_master[i]->modified || svs.time > svs.masterResolveTime[i])
if (vr_master[i]->modified || svs.time > svs.masterResolveTime[i])
{
sv_master[i]->modified = qfalse;
vr_master[i]->modified = qfalse;
svs.masterResolveTime[i] = svs.time + MASTERDNS_MSEC;
if(netenabled & NET_ENABLEV4)
{
Com_Printf("Resolving %s (IPv4)\n", sv_master[i]->string);
res = NET_StringToAdr(sv_master[i]->string, &adr[i][0], NA_IP);
Com_Printf("Resolving %s (IPv4)\n", vr_master[i]->string);
res = NET_StringToAdr(vr_master[i]->string, &adr[i][0], NA_IP);
if(res == 2)
{
@ -284,15 +284,15 @@ void SV_MasterHeartbeat(const char *message)
}
if(res)
Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][0]));
Com_Printf( "%s resolved to %s\n", vr_master[i]->string, NET_AdrToStringwPort(adr[i][0]));
else
Com_Printf( "%s has no IPv4 address.\n", sv_master[i]->string);
Com_Printf( "%s has no IPv4 address.\n", vr_master[i]->string);
}
if(netenabled & NET_ENABLEV6)
{
Com_Printf("Resolving %s (IPv6)\n", sv_master[i]->string);
res = NET_StringToAdr(sv_master[i]->string, &adr[i][1], NA_IP6);
Com_Printf("Resolving %s (IPv6)\n", vr_master[i]->string);
res = NET_StringToAdr(vr_master[i]->string, &adr[i][1], NA_IP6);
if(res == 2)
{
@ -301,9 +301,9 @@ void SV_MasterHeartbeat(const char *message)
}
if(res)
Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][1]));
Com_Printf( "%s resolved to %s\n", vr_master[i]->string, NET_AdrToStringwPort(adr[i][1]));
else
Com_Printf( "%s has no IPv6 address.\n", sv_master[i]->string);
Com_Printf( "%s has no IPv6 address.\n", vr_master[i]->string);
}
}
@ -313,7 +313,7 @@ void SV_MasterHeartbeat(const char *message)
}
Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string );
Com_Printf ("Sending heartbeat to %s\n", vr_master[i]->string );
// this command should be changed if the server info / status format
// ever incompatably changes

View file

@ -2488,11 +2488,11 @@ static qboolean UI_NetSource_HandleKey(int flags, float *special, int key) {
if(ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5)
{
char masterstr[2], cvarname[sizeof("sv_master1")];
char masterstr[2], cvarname[sizeof("vr_master1")];
while(ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5)
{
Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", ui_netSource.integer - UIAS_GLOBAL0);
Com_sprintf(cvarname, sizeof(cvarname), "vr_master%d", ui_netSource.integer - UIAS_GLOBAL0);
trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr));
if(*masterstr)
break;
@ -3223,6 +3223,18 @@ static void UI_Update(const char *name) {
trap_Cvar_Set("vr_button_map_RTHUMBLEFT_ALT", ""); // unmapped
break;
}
} else if (Q_stricmp(name, "vr_switchThumbsticks") == 0) {
if (val) {
trap_Cvar_Set("vr_button_map_A", "+button2"); // Use Item
trap_Cvar_Set("vr_button_map_B", "+button3"); // Gesture
trap_Cvar_Set("vr_button_map_X", "+moveup"); // Jump
trap_Cvar_Set("vr_button_map_Y", "+movedown"); // Crouch
} else {
trap_Cvar_Set("vr_button_map_A", "+moveup"); // Jump
trap_Cvar_Set("vr_button_map_B", "+movedown"); // Crouch
trap_Cvar_Set("vr_button_map_X", "+button2"); // Use Item
trap_Cvar_Set("vr_button_map_Y", "+button3"); // Gesture
}
} else if (Q_stricmp(name, "vr_uturn") == 0) {
int controlSchema = (int)trap_Cvar_VariableValue( "vr_controlSchema" ) % 3;
if (val) {

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;
@ -31,6 +35,7 @@ cvar_t *vr_weaponPitch = NULL;
cvar_t *vr_twoHandedWeapons = NULL;
cvar_t *vr_showItemInHand = NULL;
cvar_t *vr_refreshrate = NULL;
cvar_t *vr_superSampling = NULL;
cvar_t *vr_weaponScope = NULL;
cvar_t *vr_rollWhenHit = NULL;
cvar_t *vr_hudYOffset = NULL;
@ -46,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 )
@ -76,6 +155,7 @@ void VR_InitCvars( void )
vr_twoHandedWeapons = Cvar_Get ("vr_twoHandedWeapons", "1", CVAR_ARCHIVE);
vr_showItemInHand = Cvar_Get ("vr_showItemInHand", "1", CVAR_ARCHIVE);
vr_refreshrate = Cvar_Get ("vr_refreshrate", "72", CVAR_ARCHIVE);
vr_superSampling = Cvar_Get ("vr_superSampling", "1.1", CVAR_ARCHIVE);
vr_weaponScope = Cvar_Get ("vr_weaponScope", "1", CVAR_ARCHIVE);
vr_rollWhenHit = Cvar_Get ("vr_rollWhenHit", "0", CVAR_ARCHIVE);
vr_hudYOffset = Cvar_Get ("vr_hudYOffset", "0", CVAR_ARCHIVE);
@ -161,14 +241,25 @@ void VR_InitCvars( void )
Cvar_Get ("vr_button_map_RTHUMBLEFT_ALT", "", CVAR_ARCHIVE); // unmapped
}
// Map face buttons based on thumbstick assigned to movement
// (cannot move and jump/crouch with same thumb at same time)
qboolean switchThumbsticks = Cvar_VariableValue( "vr_switchThumbsticks" ) != 0;
if (switchThumbsticks) {
Cvar_Get ("vr_button_map_A", "+button2", CVAR_ARCHIVE); // Use Item
Cvar_Get ("vr_button_map_B", "+button3", CVAR_ARCHIVE); // Gesture
Cvar_Get ("vr_button_map_X", "+moveup", CVAR_ARCHIVE); // Jump
Cvar_Get ("vr_button_map_Y", "+movedown", CVAR_ARCHIVE); // Crouch
} else {
Cvar_Get ("vr_button_map_A", "+moveup", CVAR_ARCHIVE); // Jump
Cvar_Get ("vr_button_map_B", "+movedown", CVAR_ARCHIVE); // Crouch
Cvar_Get ("vr_button_map_X", "+button2", CVAR_ARCHIVE); // Use Item
Cvar_Get ("vr_button_map_Y", "+button3", CVAR_ARCHIVE); // Gesture
}
//Remaining button mapping (buttons not affected by schemas)
Cvar_Get ("vr_button_map_A", "+moveup", CVAR_ARCHIVE); // Jump
Cvar_Get ("vr_button_map_A_ALT", "", CVAR_ARCHIVE); // unmapped
Cvar_Get ("vr_button_map_B", "+movedown", CVAR_ARCHIVE); // Crouch
Cvar_Get ("vr_button_map_B_ALT", "", CVAR_ARCHIVE); // unmapped
Cvar_Get ("vr_button_map_X", "+button2", CVAR_ARCHIVE); // Use Item
Cvar_Get ("vr_button_map_X_ALT", "", CVAR_ARCHIVE); // unmapped
Cvar_Get ("vr_button_map_Y", "+button3", CVAR_ARCHIVE); // Gesture
Cvar_Get ("vr_button_map_Y_ALT", "", CVAR_ARCHIVE); // unmapped
Cvar_Get ("vr_button_map_SECONDARYTHUMBSTICK", "+scores", CVAR_ARCHIVE); // Scoreboard
Cvar_Get ("vr_button_map_SECONDARYTHUMBSTICK_ALT", "", CVAR_ARCHIVE); // unmapped
@ -180,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

@ -63,6 +63,9 @@ typedef struct {
int *menuCursorX;
int *menuCursorY;
qboolean menuLeftHanded;
float recenterYaw;
float superSampling;
} vr_clientinfo_t;
#endif //vr_clientinfo_h

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)
{
@ -545,20 +889,39 @@ static void IN_VRController( qboolean isRightController, ovrTracking remoteTrack
if (vr_twoHandedWeapons->integer && vr.weapon_stabilised)
{
//Apply smoothing to the weapon hand
vec3_t smooth_weaponoffset;
VectorAdd(vr.weaponoffset, vr.weaponoffset_last[0], smooth_weaponoffset);
VectorAdd(smooth_weaponoffset, vr.weaponoffset_last[1],smooth_weaponoffset);
VectorScale(smooth_weaponoffset, 1.0f/3.0f, smooth_weaponoffset);
if (vr_twoHandedWeapons->integer == 2) // Virtual gun stock
{
// Offset to the appropriate eye a little bit
vec2_t xy;
rotateAboutOrigin(Cvar_VariableValue("cg_stereoSeparation") / 2.0f, 0.0f, -vr.hmdorientation[YAW], xy);
float x = vr.offhandposition[0] - (vr.hmdposition[0] + xy[0]);
float y = vr.offhandposition[1] - (vr.hmdposition[1] - 0.1f); // Use a point lower
float z = vr.offhandposition[2] - (vr.hmdposition[2] + xy[1]);
vec3_t vec;
VectorSubtract(vr.offhandoffset, smooth_weaponoffset, vec);
float zxDist = length(x, z);
float zxDist = length(vec[0], vec[2]);
if (zxDist != 0.0f && z != 0.0f) {
VectorSet(vr.weaponangles, -degrees(atanf(y / zxDist)),
-degrees(atan2f(x, -z)), 0);
}
}
else // Basic two-handed
{
// Apply smoothing to the weapon hand
vec3_t smooth_weaponoffset;
VectorAdd(vr.weaponoffset, vr.weaponoffset_last[0], smooth_weaponoffset);
VectorAdd(smooth_weaponoffset, vr.weaponoffset_last[1],smooth_weaponoffset);
VectorScale(smooth_weaponoffset, 1.0f/3.0f, smooth_weaponoffset);
if (zxDist != 0.0f && vec[2] != 0.0f) {
VectorSet(vr.weaponangles, -degrees(atanf(vec[1] / zxDist)),
-degrees(atan2f(vec[0], -vec[2])), vr.weaponangles[ROLL] / 2.0f); //Dampen roll on stabilised weapon
vec3_t vec;
VectorSubtract(vr.offhandoffset, smooth_weaponoffset, vec);
float zxDist = length(vec[0], vec[2]);
if (zxDist != 0.0f && vec[2] != 0.0f) {
VectorSet(vr.weaponangles, -degrees(atanf(vec[1] / zxDist)),
-degrees(atan2f(vec[0], -vec[2])), vr.weaponangles[ROLL] / 2.0f); // Dampen roll on stabilised weapon
}
}
}
}
@ -731,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
@ -948,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 )
@ -958,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();
@ -986,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>
@ -30,9 +19,33 @@
#include <GLES3/gl32.h>
#endif
#define SUPER_SAMPLE 1.15f
#define DEFAULT_SUPER_SAMPLING 1.1f
extern vr_clientinfo_t vr;
extern cvar_t *vr_heightAdjust;
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)
@ -68,14 +81,97 @@ 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");
if (vr.superSampling == 0.0f || configuredSuperSampling != vr.superSampling) {
vr.superSampling = configuredSuperSampling;
if (vr.superSampling != 0.0f) {
Cbuf_AddText( "vid_restart\n" );
}
}
if (vr.superSampling == 0.0f) {
superSampling = DEFAULT_SUPER_SAMPLING;
} else {
superSampling = vr.superSampling;
}
if (engine)
{
*pWidth = width = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH) * SUPER_SAMPLE;
*pHeight = height = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT) * SUPER_SAMPLE;
// Enumerate the viewport configurations.
uint32_t viewportConfigTypeCount = 0;
OXR(xrEnumerateViewConfigurations(
engine->appState.Instance, engine->appState.SystemId, 0, &viewportConfigTypeCount, NULL));
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);
XrViewConfigurationType* viewportConfigurationTypes =
(XrViewConfigurationType*)malloc(viewportConfigTypeCount * sizeof(XrViewConfigurationType));
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
{
@ -85,13 +181,53 @@ void VR_GetResolution(engine_t* engine, int *pWidth, int *pHeight)
}
}
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
@ -99,305 +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;
}
int eyeW, eyeH;
VR_GetResolution(engine, &eyeW, &eyeH);
// 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;
if (VR_useScreenLayer() ||
(cl.snap.ps.pm_flags & PMF_FOLLOW && vr.follow_mode == VRFM_FIRSTPERSON))
{
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, eyeW, eyeW * 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], eyeW, eyeH);
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
{
vr.menuYaw = vr.hmdorientation[YAW];
// Update HMD and controllers
IN_VRUpdateHMD( invViewTransform[0] );
IN_VRUpdateControllers( invViewTransform[0], frameState.predictedDisplayTime );
IN_VRSyncActions();
ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2();
layer.HeadPose = engine->tracking.HeadPose;
//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 );
const ovrMatrix4f defaultProjection = ovrMatrix4f_CreateProjectionFov(
fov_x, fov_y, 0.0f, 0.0f, 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);
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;
ovrFramebuffer_Acquire(frameBuffer);
ovrFramebuffer_SetCurrent(frameBuffer);
VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height);
Com_Frame();
VR_ClearFrameBuffer(engine->framebuffers.framebuffers[engine->framebuffers.swapchainIndex], eyeW, eyeH);
// 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);
re.SetVRHeadsetParms(&projectionMatrix, &monoVRMatrix,
engine->framebuffers.framebuffers[engine->framebuffers.swapchainIndex]);
ovrFramebuffer_Resolve(frameBuffer);
ovrFramebuffer_Release(frameBuffer);
ovrFramebuffer_SetNone();
Com_Frame();
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];
engine->framebuffers.swapchainIndex = (engine->framebuffers.swapchainIndex + 1) %
engine->framebuffers.swapchainLength;
if (fullscreenMode) {
VR_ReInitRenderer();
fullscreenMode = qfalse;
}
const ovrLayerHeader2* layers[] = {
&layer.Header
};
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer;
ovrSubmitFrameDescription2 frameDesc = { 0 };
frameDesc.Flags = 0;
frameDesc.SwapInterval = 1;
frameDesc.FrameIndex = engine->frameIndex;
frameDesc.DisplayTime = engine->predictedDisplayTime;
frameDesc.LayerCount = 1;
frameDesc.Layers = layers;
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;
vrapi_SubmitFrame2(engine->ovr, &frameDesc);
}
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;
engine->appState.Layers[engine->appState.LayerCount++].Projection = projection_layer;
} else {
fullscreenMode = qtrue;
// 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;
engine->appState.Layers[engine->appState.LayerCount++].Cylinder = cylinder_layer;
}
// 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];
}
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;
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

@ -1,16 +1,20 @@
package com.drbeef.ioq3quest;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
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;
import com.drbeef.externalhapticsservice.HapticServiceClient;
import com.drbeef.externalhapticsservice.HapticsConstants;
import org.libsdl.app.SDLActivity;
@ -24,6 +28,7 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@ -31,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;
@ -40,7 +46,11 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
String commandLineParams;
private HapticServiceClient externalHapticsServiceClient = null;
private Vector<HapticServiceClient> externalHapticsServiceClients = new Vector<>();
//Use a vector of pairs, it is possible a given package _could_ in the future support more than one haptic service
//so a map here of Package -> Action would not work.
private static Vector<Pair<String, String>> externalHapticsServiceDetails = new Vector<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -53,6 +63,27 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
}
@Override protected void onDestroy()
{
Log.i(TAG, "onDestroy called");
for (HapticServiceClient externalHapticsServiceClient : externalHapticsServiceClients) {
externalHapticsServiceClient.stopBinding();
}
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.
@ -77,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) {
}
}
}
@ -88,26 +121,32 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
}
public void create() throws IOException {
//Make the directories
// Prepare base game directory
new File("/sdcard/ioquake3Quest/baseq3").mkdirs();
new File("/sdcard/ioquake3Quest/missionpack").mkdirs();
//Copy the command line params file
// Copy the command line params file and autoexec
copy_asset("/sdcard/ioquake3Quest", "commandline.txt", false);
copy_asset("/sdcard/ioquake3Quest/baseq3", "autoexec.cfg", false);
copy_asset("/sdcard/ioquake3Quest/missionpack", "autoexec.cfg", false);
//copy demo
copy_asset("/sdcard/ioquake3Quest/baseq3", "pak0.pk3", false);
//our special pak files
// Copy our special pak file and demo
copy_asset("/sdcard/ioquake3Quest/baseq3", "pakQ3Q.pk3", true);
copy_asset("/sdcard/ioquake3Quest/missionpack", "pakQ3Q.pk3", true);
copy_asset("/sdcard/ioquake3Quest/baseq3", "pak0.pk3", false);
//Copy Omarlego's excellent replacement background
copy_asset("/sdcard/ioquake3Quest/baseq3", "z_custom_background66.pk3", false);
// Cleanup incompatible shaders
delete_asset("/sdcard/ioquake3Quest/baseq3/glsl");
//If open arena is installed then copy necessary stuff
// If Team Arena is installed then copy necessary stuff
if (new File("/sdcard/ioquake3Quest/missionpack").exists()) {
copy_asset("/sdcard/ioquake3Quest/missionpack", "autoexec.cfg", false);
copy_asset("/sdcard/ioquake3Quest/missionpack", "pakQ3Q.pk3", true);
delete_asset("/sdcard/ioquake3Quest/missionpack/glsl");
}
// If Open Arena is installed then copy necessary stuff
if (new File("/sdcard/ioquake3Quest/baseoa").exists()) {
copy_asset("/sdcard/ioquake3Quest/baseoa", "autoexec_oa.cfg", "autoexec.cfg", false);
copy_asset("/sdcard/ioquake3Quest/baseoa", "pakQ3Q.pk3", true);
delete_asset("/sdcard/ioquake3Quest/baseoa/glsl");
}
//Read these from a file and pass through
@ -141,11 +180,15 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
} catch (Exception e) {
}
externalHapticsServiceClient = new HapticServiceClient(this, (state, desc) -> {
Log.v(TAG, "ExternalHapticsService is:" + desc);
});
for (Pair<String, String> serviceDetail : externalHapticsServiceDetails) {
HapticServiceClient client = new HapticServiceClient(this, (state, desc) -> {
Log.v(TAG, "ExternalHapticsService " + serviceDetail.second + ": " + desc);
}, new Intent(serviceDetail.second)
.setPackage(serviceDetail.first));
externalHapticsServiceClient.bindService();
client.bindService();
externalHapticsServiceClients.add(client);
}
Log.d(TAG, "nativeCreate");
nativeCreate(this);
@ -167,6 +210,23 @@ public class MainActivity extends SDLActivity // implements KeyEvent.Callback
}
}
public void delete_asset(String path) {
File file = new File(path);
delete_asset(file);
}
public void delete_asset(File file) {
if (!file.exists()) {
return;
}
if (file.isDirectory()) {
for (File nestedFile : file.listFiles()) {
delete_asset(nestedFile);
}
}
file.delete();
}
public void _copy_asset(String name_in, String name_out) {
AssetManager assets = this.getAssets();
@ -199,42 +259,52 @@ 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");
//Add possible external haptic service details here
externalHapticsServiceDetails.add(Pair.create(HapticsConstants.BHAPTICS_PACKAGE, HapticsConstants.BHAPTICS_ACTION_FILTER));
externalHapticsServiceDetails.add(Pair.create(HapticsConstants.FORCETUBE_PACKAGE, HapticsConstants.FORCETUBE_ACTION_FILTER));
}
public void haptic_event(String event, int position, int flags, int intensity, float angle, float yHeight) {
if (externalHapticsServiceClient.hasService()) {
try {
if (!hapticsEnabled)
{
externalHapticsServiceClient.getHapticsService().hapticEnable();
hapticsEnabled = true;
return;
}
boolean areHapticsEnabled = hapticsEnabled;
for (HapticServiceClient externalHapticsServiceClient : externalHapticsServiceClients) {
if (event.compareTo("frame_tick") == 0)
{
externalHapticsServiceClient.getHapticsService().hapticFrameTick();
}
if (externalHapticsServiceClient.hasService()) {
try {
//Enabled all haptics services if required
if (!areHapticsEnabled)
{
externalHapticsServiceClient.getHapticsService().hapticEnable();
hapticsEnabled = true;
continue;
}
//Use the Doom3Quest haptic patterns for now
String app = "Doom3Quest";
if (event.contains(":"))
{
String[] items = event.split(":");
app = items[0];
event = items[1];
if (event.compareTo("frame_tick") == 0)
{
externalHapticsServiceClient.getHapticsService().hapticFrameTick();
}
//Uses the Doom3Quest and RTCWQuest haptic patterns
String app = "Doom3Quest";
String eventID = event;
if (event.contains(":"))
{
String[] items = event.split(":");
app = items[0];
eventID = items[1];
}
externalHapticsServiceClient.getHapticsService().hapticEvent(app, eventID, position, flags, intensity, angle, yHeight);
}
catch (RemoteException r)
{
Log.v(TAG, r.toString());
}
externalHapticsServiceClient.getHapticsService().hapticEvent(app, event, position, flags, intensity, angle, yHeight);
}
catch (RemoteException r)
{
Log.v(TAG, r.toString());
}
}
}
}

View file

@ -69,9 +69,10 @@ itemDef {
itemDef {
name controls3
group grpControls3
type ITEM_TYPE_YESNO
type ITEM_TYPE_MULTI
text "Two-Handed Weapons:"
cvar "vr_twoHandedWeapons"
cvarFloatList { "Disabled" 0 "Enabled (Basic)" 1 "Enabled (VR Gun Stock)" 2}
rect 99 125 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
@ -157,6 +158,7 @@ itemDef {
textscale .333
forecolor 1 1 1 1
visible 1
action { uiScript update "vr_switchThumbsticks" }
}
itemDef {

View file

@ -191,9 +191,10 @@ itemDef {
itemDef {
name controls
group grpControls
type ITEM_TYPE_YESNO
type ITEM_TYPE_MULTI
text "Two-Handed Weapons:"
cvar "vr_twoHandedWeapons"
cvarFloatList { "Disabled" 0 "Enabled (Basic)" 1 "Enabled (VR Gun Stock)" 2}
rect 30 56 200 20
textalign ITEM_ALIGN_RIGHT
textalignx 143
@ -279,6 +280,7 @@ itemDef {
textscale .25
forecolor 1 1 1 1
visible 1
action { uiScript update "vr_switchThumbsticks" }
}
itemDef {

View file

@ -184,7 +184,7 @@ itemDef {
type ITEM_TYPE_MULTI
text "Refresh Rate:"
cvar "vr_refreshrate"
cvarFloatList { "60" 60 "72 (Recommended)" 72 "80" 80 "90" 90 }
cvarFloatList { "60" 60 "72" 72 "80" 80 "90" 90 "120" 120 }
rect 0 50 306 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
@ -194,13 +194,29 @@ itemDef {
visible 0
}
itemDef {
name graphics
group grpSystem
type ITEM_TYPE_MULTI
text "Supersampling:"
cvar "vr_superSampling"
cvarFloatList { "0.8" 0.8 "0.9" 0.9 "1.0" 1.0 "1.1" 1.1 "1.2" 1.2 "1.3" 1.3 }
rect 0 70 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
textscale .25
forecolor 1 1 1 1
visible 0
}
itemDef {
name graphics
group grpSystem
type ITEM_TYPE_SLIDER
text "Brightness:"
cvarfloat "r_gamma" 0.05 0.8 1.2
rect 0 70 256 20
cvarfloat "r_gamma" 0.05 0.6 1.0
rect 0 90 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -216,7 +232,7 @@ itemDef {
text "Railgun Effect:"
cvar "cg_oldRail"
cvarFloatList { "Q2 Style" 0 "Q3 Style" 1 }
rect 0 90 256 20
rect 0 110 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -232,7 +248,7 @@ itemDef {
text "Lighting:"
cvar "r_vertexlight"
cvarFloatList { "Lightmap (High)" 0 "Vertex (Low)" 1 }
rect 0 110 256 20
rect 0 130 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -249,7 +265,7 @@ itemDef {
text "Opponent Shadows:"
cvar "cg_shadows"
cvarFloatList { "None" 0 "Low" 1 "High" 3 }
rect 0 130 306 20
rect 0 150 306 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -265,7 +281,7 @@ itemDef {
text "Player Shadow:"
cvar "cg_playerShadow"
cvarFloatList { "None" 0 "Low" 1 "High" 3 }
rect 0 150 306 20
rect 0 170 306 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -281,7 +297,7 @@ itemDef {
text "Geometric Detail:"
cvar "r_lodbias"
cvarFloatList { "High" -1 "Medium" 1 "Low" 2 }
rect 0 170 256 20
rect 0 190 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -298,7 +314,7 @@ itemDef {
text "Texture Detail:"
cvar "r_picmip"
cvarFloatList { "Low" 2 "Normal" 1 "High" 0 }
rect 0 190 256 20
rect 0 210 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -314,7 +330,7 @@ itemDef {
type ITEM_TYPE_YESNO
text "Compress Textures:"
cvar "r_ext_compressed_textures"
rect 0 210 256 20
rect 0 230 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17
@ -330,7 +346,7 @@ itemDef {
type ITEM_TYPE_YESNO
text "Low Quality Sky:"
cvar "r_fastsky"
rect 0 230 256 20
rect 0 250 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 133
textaligny 17

View file

@ -87,7 +87,7 @@ itemDef {
type ITEM_TYPE_MULTI
text "Refresh Rate:"
cvar "vr_refreshrate"
cvarFloatList { "60" 60 "72 (Recommended)" 72 "80" 80 "90" 90 }
cvarFloatList { "60" 60 "72" 72 "80" 80 "90" 90 "120" 120 }
rect 99 42 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
@ -97,13 +97,29 @@ itemDef {
visible 0
}
itemDef {
name graphics
group grpSystem
type ITEM_TYPE_MULTI
text "Supersampling:"
cvar "vr_superSampling"
cvarFloatList { "0.8" 0.8 "0.9" 0.9 "1.0" 1.0 "1.1" 1.1 "1.2" 1.2 "1.3" 1.3 }
rect 99 67 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
textscale .333
forecolor 1 1 1 1
visible 0
}
itemDef {
name graphics
group grpSystem
type ITEM_TYPE_SLIDER
text "Brightness:"
cvarfloat "r_gamma" 0.05 0.8 1.2
rect 99 67 256 20
cvarfloat "r_gamma" 0.05 0.6 1.0
rect 99 92 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -119,7 +135,7 @@ itemDef {
text "Railgun Effect:"
cvar "cg_oldRail"
cvarFloatList { "Q2 Style" 0 "Q3 Style" 1 }
rect 99 92 256 20
rect 99 117 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -135,7 +151,7 @@ itemDef {
text "Lighting:"
cvar "r_vertexlight"
cvarFloatList { "Lightmap (High)" 0 "Vertex (Low)" 1 }
rect 99 117 256 20
rect 99 142 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -152,7 +168,7 @@ itemDef {
text "Opponent Shadows:"
cvar "cg_shadows"
cvarFloatList { "None" 0 "Low" 1 "High" 3 }
rect 99 142 256 20
rect 99 167 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -168,7 +184,7 @@ itemDef {
text "Player Shadow:"
cvar "cg_playerShadow"
cvarFloatList { "None" 0 "Low" 1 "High" 3 }
rect 99 167 256 20
rect 99 192 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -184,7 +200,7 @@ itemDef {
text "Geometric Detail:"
cvar "r_lodbias"
cvarFloatList { "High" -1 "Medium" 1 "Low" 2 }
rect 99 192 256 20
rect 99 217 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -201,7 +217,7 @@ itemDef {
text "Texture Detail:"
cvar "r_picmip"
cvarFloatList { "Low" 2 "Normal" 1 "High" 0 }
rect 99 217 256 20
rect 99 242 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -217,7 +233,7 @@ itemDef {
type ITEM_TYPE_YESNO
text "Compress Textures:"
cvar "r_ext_compressed_textures"
rect 99 242 256 20
rect 99 267 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20
@ -233,7 +249,7 @@ itemDef {
type ITEM_TYPE_YESNO
text "Low Quality Sky:"
cvar "r_fastsky"
rect 99 267 256 20
rect 99 292 256 20
textalign ITEM_ALIGN_RIGHT
textalignx 128
textaligny 20

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ioquake3Quest</string>
<string name="app_name">Quake3Quest</string>
</resources>

View file

@ -3,7 +3,7 @@
setlocal
set BUILD_TYPE=release
set VERSION=0.31.4-multiview
set VERSION=1.1.3
@REM Define the following environment variables to sign a release build
@REM set KEYSTORE=