RTCWQuest is a standalone Android VR port of the Return to Castle Wolfenstein single-player campaign. The current development branch has been migrated away from the original Oculus Mobile SDK / VrApi backend and now uses OpenXR with an arm64-only Android build.
This repository contains the engine, Android wrapper, VR integration, and the project's VR assets. It does not contain the full commercial RTCW game data.
The current Android build is:
- OpenXR based, using the Khronos Android OpenXR loader.
- arm64-v8a only.
- Built with Android Gradle Plugin 8.2.1 and NDK 25.1.8937393.
- Targeted at Android API 32 with a minimum API level of 29.
- Intended for standalone Android OpenXR headsets, including Meta Quest and Pico.
- Still using the RTCW/id Tech 3 engine structure, including native game modules
for
qagame,cgame, andui.
The old VrApi/Oculus Mobile SDK build path is deprecated and should not be used for this branch.
Important directories:
Projects/Android- Android Gradle project.Projects/Android/build.gradle- main Android build configuration.Projects/Android/AndroidManifest.xml- app manifest, OpenXR permissions, headset launch categories, and runtime metadata.Projects/Android/jni- native Android/NDK build root.Projects/Android/jni/RTCWVR- VR layer, OpenXR backend, input, frame setup, and Android surface integration.Projects/Android/jni/rtcw- RTCW engine, game, cgame, ui, renderer, and Android native build files.Projects/Android/jni/SupportLibs/gl4es- gl4es support library.Projects/Android/z_vr_assets- VR asset source folder packed intoassets/z_vr_assets.pk3during Gradle builds.java/com/drbeef/rtcwquest- Java activity and native library bootstrap.assets- packaged APK assets.res- Android resources.
Install:
- Android Studio or command-line Android SDK tools.
- Android SDK Platform 32.
- Android NDK
25.1.8937393. - Java runtime compatible with Android Gradle Plugin 8.2.1.
- ADB for installing and launching on a headset.
The Gradle wrapper is included in Projects/Android, so a separate Gradle
installation is not required.
Create or update:
Projects/Android/local.properties
Example:
sdk.dir=C\:\\Users\\yourname\\AppData\\Local\\Android\\Sdk
ndk.dir=C\:\\Users\\yourname\\AppData\\Local\\Android\\Sdk\\ndk\\25.1.8937393Use paths that match your machine. Do not commit personal SDK paths.
From the repository root:
cd Projects\Android
.\gradlew.bat assembleDebugThe debug APK is written to:
Projects/Android/build/outputs/apk/debug/rtcwquest-debug.apk
For a release build:
cd Projects\Android
.\gradlew.bat assembleReleaseThe release APK is written to:
Projects/Android/build/outputs/apk/release/rtcwquest-release.apk
The default debug and release signing configs use
Projects/Android/android.debug.keystore unless Gradle properties are supplied:
key.store=...
key.store.password=...
key.alias=...
key.alias.password=...Connect the headset with developer mode enabled, then run:
adb devices
adb install -r -d Projects\Android\build\outputs\apk\debug\rtcwquest-debug.apkLaunch from the headset UI, or from ADB:
adb shell monkey -p com.drbeef.rtcwquest -c android.intent.category.LAUNCHER 1For clean testing:
adb shell am force-stop com.drbeef.rtcwquest
adb logcat -c
adb shell monkey -p com.drbeef.rtcwquest -c android.intent.category.LAUNCHER 1Useful logcat filters:
adb logcat RTCWVR:* OpenXR:* AndroidRuntime:* libc:* *:SThis is an engine port. The full commercial game data is not included.
To play the full game, install RTCW on a PC and copy the relevant .pk3 files
from the installed RTCW game folder to the headset's RTCWQuest data directory.
The app creates its folders after the first launch.
Typical headset path:
/sdcard/RTCWQuest/Main
If the folder does not appear immediately over USB/MTP, launch the app once and restart or reconnect the headset.
The original RTCWQuest build was Quest-only and used Oculus VrApi. This branch uses OpenXR instead.
Key points:
- Java loads
openxr_loaderbeforertcw_client. - Gradle depends on:
org.khronos.openxr:openxr_loader_for_android:1.1.60- The build extracts the loader AAR's arm64 shared library into generated
jniLibs:
Projects/Android/build/generated/openxr-loader/jniLibs/arm64-v8a/libopenxr_loader.so
Projects/Android/jni/Android.mkimports that generatedlibopenxr_loader.soas a prebuilt shared library.- The native client links against
openxr_loaderandgl4es. - The manifest declares OpenXR permissions, runtime broker queries, and immersive headset launch categories.
- OpenXR runtime selection is handled by the platform runtime broker on the headset.
The native OpenXR code lives primarily in:
Projects/Android/jni/RTCWVR/TBXR_Common.cProjects/Android/jni/RTCWVR/TBXR_Common.hProjects/Android/jni/RTCWVR/OpenXrInput.cProjects/Android/jni/RTCWVR/RTCWVR_SurfaceView.c
The OpenXR backend initializes the Android OpenXR loader, creates the instance and session, uses OpenGL ES swapchains, and drives the frame lifecycle used by the existing RTCW VR hooks.
This branch is arm64-only. The build intentionally does not produce
armeabi-v7a output.
Relevant build settings:
Projects/Android/build.gradleabiFilters 'arm64-v8a'minSdkVersion 29targetSdkVersion 32compileSdkVersion 32ndkVersion '25.1.8937393'
Projects/Android/jni/Application.mkAPP_ABI := arm64-v8aAPP_MODULES := gl4es qagame ui cgame rtcw_client
The native game modules are built as normal shared libraries:
libqagame.solibcgame.solibui.solibrtcw_client.solibgl4es.solibopenxr_loader.so
The 64-bit conversion requires care around the VM/native DLL boundary. In this
codebase, pointer-sized values crossing module boundaries must use pointer-sized
types such as intptr_t; do not reintroduce assumptions that pointers fit in
int. QVM bytecode and interpreted VM storage remain 32-bit where the original
engine format requires it.
When touching VM, syscall, or module-loading code, check for:
- Pointer-to-
intcasts. - Function pointer casts through integer types.
- Syscall argument arrays using
int *where native code needsintptr_t *. VM_Call,vmMain, anddllEntrysignatures.- Savegame compatibility code that reads historical 32-bit structures.
The app uses the RTCWQuest external data folder for user data and game data. Existing 32-bit-era saves may need compatibility handling because native struct sizes and pointer-sized fields changed in the arm64 build. The project contains compatibility work for older save data, but any further savegame changes should be tested against both newly-created arm64 saves and old user saves.
Recommended save testing:
- Start a new game, save, quit, relaunch, and load.
- Quick save and quick load.
- Load a legacy 32-bit save.
- Transition levels after loading.
- Verify cgame, game, and UI modules agree on struct sizes.
The renderer still uses the RTCW/id Tech 3 rendering flow with gl4es and an OpenGL ES backend. OpenXR owns headset frame timing and swapchain presentation.
Important behavior:
- Immersive gameplay is rendered stereo.
- Menus, loading, console, and non-immersive states use a virtual screen path.
- Gameplay HUD is drawn through the normal cgame 2D draw path rather than as an OpenXR compositor overlay.
- Scope and binocular FOV handling must keep the game/culling FOV and submitted OpenXR projection FOV in sync.
When changing FOV, culling, cinematic cameras, sky rendering, or HUD projection, test all of these:
- Normal gameplay.
- Cinematic cutscenes.
- Loading screens.
- Console/menu virtual screen.
- Binocular zoom.
- Scoped weapons.
- Head pitch/yaw/roll.
- Skybox behavior.
OpenXR actions are translated into the existing RTCWQuest VR input state and then into engine commands. The goal is to preserve legacy RTCWQuest controls while using OpenXR-backed controller poses and buttons.
Important gameplay behaviors to preserve:
- Dominant-hand weapon aiming.
- Left-handed mode.
- Snap and smooth turn.
- Weapon stabilization / virtual stock.
- Scope engagement.
- Binoculars.
- Backpack weapon selection.
- Grenade throwing.
- Mounted guns.
- Teleport.
- External haptics service events plus OpenXR controller haptics.
A successful APK should include only arm64-v8a native libraries. Useful check:
cd Projects\Android
.\gradlew.bat assembleDebug
jar tf build\outputs\apk\debug\rtcwquest-debug.apk | findstr /i "lib/.*\\.so"Expected native libraries include:
lib/arm64-v8a/librtcw_client.solib/arm64-v8a/libqagame.solib/arm64-v8a/libcgame.solib/arm64-v8a/libui.solib/arm64-v8a/libgl4es.solib/arm64-v8a/libopenxr_loader.so
There should be no armeabi-v7a libraries in the APK.
Check Projects/Android/local.properties. Make sure sdk.dir and ndk.dir
point to installed SDK/NDK folders.
Check that the headset has an OpenXR runtime installed and enabled. Pull logs:
adb logcat RTCWVR:* OpenXR:* AndroidRuntime:* libc:* *:SVerify libopenxr_loader.so is present in the APK and that Java is loading it
before rtcw_client.
Check:
- Manifest OpenXR permissions and categories.
- Runtime broker availability on the headset.
adb logcatfor OpenXR initialization errors.- That the build contains only arm64 libraries on an arm64 headset.
Launch once to create /sdcard/RTCWQuest/Main, then copy the full RTCW .pk3
files into that folder.
Check whether the save was produced by the older 32-bit build or the current arm64 build. Legacy saves may require compatibility conversion. Use logcat to inspect reported struct sizes and savegame version checks.
For future work:
- Do not reintroduce Oculus VrApi or Oculus Mobile SDK dependencies.
- Keep the Android build arm64-only unless there is a deliberate reason to add another ABI.
- Keep OpenXR loader packaging through the Khronos AAR unless the project makes a deliberate loader strategy change.
- Be careful around VM/native boundary types.
- Avoid pointer truncation in savegame, syscall, and VM code.
- Test visual changes in both immersive stereo and virtual screen modes.
- Test on both Meta Quest and Pico when changing OpenXR runtime, manifest, or input behavior.
RTCWQuest builds on a long chain of work from id Software, the original RTCW GPL source release, Team Beef VR ports, Beloko Android porting work, gl4es, and many project contributors.
Special thanks from the original project:
- Emile Belanger for the Android port base.
- Baggyg for VR support, assets, artwork, testing, and model work.
- VR_Bummser for testing and community support.
- The SideQuest team for sideloading support.
- Dark Matter Productions and William Faure for remastered weapon work.
- HellBaron for the Venom Mod basis.
- Johannes Tripolt (Eigenlaut) for weapon fixes and enhancements.
- ptitSeb for gl4es.
This repository is an engine/port project. RTCW game assets are owned by their respective rights holders and are not included as full commercial game content.
The OpenXR migration removes the previous dependency on the deprecated Oculus VrApi SDK path, making the VR backend friendlier to GPL distribution concerns.
