Native VR port · Meta Quest 3

Unreal Tournament,
Native on Meta Quest.

It runs on the headset. Nothing is streamed from a PC, and it isn't a flat screen floating in front of you. I rebuilt the 1999 engine as a 64-bit ARM binary so it renders real stereo through OpenXR, with the gun in your hand and the whole original game behind it.

Download How it works

Mod by GHWST  ·  @GhwstVR

The Liandri tower and city skyline from the Unreal Tournament intro, rendered on a Quest 3 under a purple night sky.

Pulled straight off the headset. Left eye buffer, 1680×1760 per eye.

What it is

The whole game, in VR, standalone

Every map, every weapon, every bot, the menus, the mod support, online play. This is actual Unreal Tournament, not something built to look like it. What I changed is everything sitting between the game and your eyes.

Real stereo

Two eyes, two separate frustums, handed to the compositor as a real projection layer. You can judge distance with it.

The gun is in your hand

The weapon is attached to your controller and fires where you point it. Shots come out of the barrel, not out of the middle of the model.

Lean, duck, peek

Moving your head moves the view in the world, so you can lean around a corner without moving your feet.

Menus in the room

The menus sit on a panel a couple of metres out, anchored where you opened them, and you point at them with a controller. The HUD stays level with the horizon no matter how you tilt your head.

Sniper scope that zooms

Alt-fire actually magnifies the view. It also switches your aim to your head and puts the crosshair dead centre, because aiming a scope with a controller at 8x is not workable.

Online play

There's a server browser, you can join live servers, and any maps or mods you don't have download and unpack on the headset by themselves.

Screenshots

Straight off the device

No mock-ups, no PC renders. These came off a Quest 3 over adb. All I did was crop them.

Get it

You bring the game, I bring the VR

This is only the engine. It ships zero Unreal Tournament content, so no maps, no textures, no sounds, no music. You need your own copy of the game and you hand it to the app yourself.

Download APK arm64-v8a · Quest 3 · sideload
HeadsetMeta Quest 3. Developer mode turned on so you can sideload.
You need to ownUnreal Tournament (1999). GOTY or any full install. It's still sold in plenty of places, but you're on your own for that part.
SpaceA full install runs about 2 GB. The importer copies your files rather than moving them, so leave room for two copies while it runs, then delete the folder you imported from. More again once you start collecting maps online.
ControllersTouch controllers. The gun follows whichever hand you set as your gun hand.
  1. Sideload the APK

    SideQuest, or adb install -r ut99quest.apk with the headset plugged in.

  2. Give it your Unreal Tournament files

    Two ways to do this. Both end up in the same place.

    Easiest: copy your UT99 folder onto the headset, anywhere you like, as a folder or as a zip. Then launch the app. It'll tell you it can't find any game data and give you buttons for Select UT99 ZIP and Select UT99 folder. Point it at what you copied, tap Use this folder and then Allow, and it copies everything into place and launches straight into the game.

    Faster if you're comfortable with adb: push the folders straight into the app's own directory and skip the picker entirely. This is where the app looks by default, and it needs no permissions and no setup.

    adb push System   /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
    adb push Maps     /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
    adb push Textures /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
    adb push Sounds   /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
    adb push Music    /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
  3. Launch it

    It comes up in stereo, in the intro cinematic. Everything after that is the normal Unreal Tournament menus and you point at them with a controller.

What it's looking for. A normal UT99 install: System, Maps, Textures, Sounds and Music, with Core.u, Engine.u and Botpack.u inside System. If you point it one folder too high or too low it'll say so and let you try again.

The game has to run from the app's own folder. Anywhere else needs storage permission, which a sideload doesn't always grant, and when it isn't granted the app can't read the files and dies at startup. Staging your copy somewhere like /sdcard/UT99Quest is fine. The importer reads it through the system file picker, which needs no permission. Just let the importer move it in rather than trying to play from there. The adb path above puts it in the right place directly.

Worth saying plainly. This is a hobby port of a 25 year old game that you sideload onto a headset. It's not a store release, there's no support line, and it will do odd things. Back up anything you care about before you point tools at it.

How it works

What actually had to change

Unreal Engine 1 was written in 1998 for a 32-bit x86 PC with one screen. Hardly any of the real work here was VR features. Most of it was getting a rendering model that predates programmable shaders to produce something a modern compositor will accept.

64-bit, on ARM

The engine assumes 32-bit pointers in places that matter, like struct layouts that got saved to disk as a file format. On 64-bit those go wrong quietly instead of failing to compile.

Stereo without a view matrix

UE1 has no view matrix, so there isn't one to give two of. The separation comes purely from where each eye sits. Anything that moves an eye has to move the head and add the eye offset back, or the 3D quietly turns flat.

The 2D layer is a quad

The HUD, console and menus are flat 2D draws. They get mapped onto a plane sitting in the world so they have real depth, and the controller pointer runs that same mapping backwards to work out what you clicked.

Optical zoom, done properly

The scope divides the eye tangents rather than the angles, because that's what magnification actually is. The field of view handed to the compositor is deliberately left alone. Narrowing both cancels the zoom out completely, which I found out the hard way.

Downloads that finish

Joining a modded server meant pulling tens of megabytes through UT99's 1999 file transfer. It now downloads over HTTP and unpacks .uz files on the headset. Last measurement was 45 packages, 24.8 MB down and 50.5 MB on disk, in under a minute.

Measured, not assumed

Frame timing is logged per eye. Standing still it sits at the display's full 90 Hz with about 7 ms of the budget spare. A live bot match on Deck16 measures 74 to 90 fps.

Status

Where it's at

It's playable and I'm playing it. It isn't finished. Here's the honest split.