DOOM 3 for iOS and tvOS for Apple TV

Pretty much the last jewel in the crown that I hadn’t ported to iOS and tvOS was DOOM 3, powered by id Tech 4.

By now you know the drill – here’s the GitHub link for those who want to cut to the chase.

DOOM 3 for iOS and tvOS for Apple TV

Every game or engine I’ve ported so far has had challenges and this one is no exception. Besides the fact that like Quake II there’s no existing ports to iOS, the game itself has two different releases, years apart: DOOM 3, the original 2004 release, and DOOM 3: BFG Edition, a 2012 re-release with enhanced visuals to reflect more modern PC hardware and later consoles. I originally wasn’t sure which one to port, or if I should do both.

After the 2000 release of the Quake III: Team Arena expansion pack, a faction within id Software wanted to do a new DOOM game. A majority of the owning partners resisted the idea, and it was out of character with id’s usual practice of coming up with a new IP. However a large enough contingent threatened to leave that management caved and the new DOOM project got the go-ahead. Carmack announced the decision as well as the precipitating drama and its aftermath (controversial employee Paul Steed was terminated) on his .plan file.

DOOM 3 would wind up being both a sequel as well as a reboot of the previous games. The original games were groundbreaking, using the best technology available at the time and generally not following first person shooter game development trends because there weren’t any. You can point to individual entries here and there as to who did what first, but there’s a reason why for many years first person shooters were called “DOOM clones”. Numerous game developers would license the source code to Wolfenstein 3-D but DOOM changed everything. Apogee, who published Wolfenstein 3-D had a developer spend two years to make another game with the engine, the Sci-Fi Blake Stone: Aliens of Gold. It was ambitious, it added a lot of things that Wolfenstein 3-D never had, and it had the exceptionally bad timing of being released one week before DOOM. I’ve used this analogy before but it’s like being that movie that got released on the same day as the original Star Wars.

In the decade between DOOM II and DOOM 3 (which, like Quake 4, for some reason moved away from Roman Numerals and went with Arabic Numerals – all the more odd when you consider the Roman Numeral version had an official logo at one point) a lot of first person shooter games had come out. Games which tried new things, like Half-Life and its long buildup with the train ride and experiment gone wrong, or System Shock 2 which melded elements of survival horror with interactive elements like a PDA and audio diaries. The designers had to contend with the notion that the gameplay elements had to mesh with the increasing graphics fidelity and resulting realism that modern gamers had grown to expect. The “monster closets” of the original games (do thing in level, trigger door to small room with monsters that now come at you) made less sense in a higher fidelity world.

Flashing forward a bit, the 2016 reboot of DOOM seems to strike an impressive balance between graphic improvements and the frantic gameplay of the originals to the point where it can make the direction they did decide to go with DOOM 3 seem like a giant misfire. In hindsight though, you can understand why they went the direction they did – the original premise of the game was that the main character was fighting his way through or out of a series of Martian bases after a failed experiment caused demons from hell to take over and turn the remaining humans into zombies. Really though they developed the gameplay and conjured up a plot around it later, and not a very thorough plot either. Plots aren’t always necessary in gaming – no one knows why Pac-Man is in a maze surrounded by ghosts, for example, and no one cares.

So when tasked to come up with a sequel/reboot of that concept, and armed with the knowledge that games like System Shock 2 have come along, it makes perfect sense that one angle you try and approach it from essentially takes the form of a survival horror game in the dark. Carmack’s experimentation with advanced lighting and shadowing techniques lent itself to a game where the lack of light played a key role – the player has a flashlight he can use to illuminate the darkness, but he can’t have it out at the same time as a weapon. Designed to increase tension, it became the game’s most controversial decision, later to be changed by mods and modified in the BFG release.

The game had other technological feats up its sleeve, such as the use of 5.1 surround sound which was uncommon on the PC at the time and gaming in general until game consoles with HDMI out started becoming a thing. Early in the development they had contracted with Trent Reznor of Nine Inch Nails to do the sound design and music, which led to several changes in how the engine handled audio since Reznor was a professional engineer (Reznor would later drop out of the project for various reasons and the theme song would be done by tool Tweaker, fronted by Chris Vrenna, a former NIN collaborator). And the level editor was actually built into the game engine, accessible via a command line option, which was something Carmack resisted for years but eventually agreed to.

id Software’s console ports were usually long after the fact afterthoughts, frequently outsourced to others. They had some hand in the SNES port of Wolfenstein 3-D, they did the Atari Jaguar port of DOOM themselves (it famously took Carmack years to find a home for the Jaguar dev kit, he wanted to make sure someone who would actually appreciate it would take it), but their ports of QuakeQuake II and Quake III: Arena were all done by other companies. The console ports of Quake and Quake II actually used different engines and the Dreamcast and PS2 ports of Quake III: Arena featured various differences with the original (with the PS2 port being renamed Quake III: Revolution).

Their plan for the console port of DOOM 3 to the original Xbox was similar in the respect that it was outsourced to a different developer (Vicarious Visions) but rather than being an after-the-fact release, it was developed alongside the PC game, though it featured cut down levels and sounds, was released several months later, but would feature co-op play, which the PC version lacked.

Apparently part of the logic of putting out DOOM 3 on the Xbox, as opposed to also putting out out on the PS2 or GameCube, was that the Xbox was more roughly analogous to a PC with its x86 architecture and Nvidia GPU. Something Carmack said nearly every year in the four or five QuakeCon keynotes delivered during DOOM 3‘s development, whose development apparently took longer than expected, was that the game would be playable on a GeForce 256 (the first GeForce card, followed later by the GeForce 2). Ergo the fact that the Xbox had the rough equivalent, reportedly, of a GeForce 3 era card made perfect sense. In practice, a GeForce 3 was the minimum stated requirement for the game and the GeForce 3 Ti 20 (I think) that I had at the time barely played the game in whatever resolution it was I was running it as. Part of that may have been the back and forth a lot of PC gamers do – namely having to make the decision of whether or not you would prefer a graphically dialed down experience in favor of a higher frame rate, or better graphics at the cost of frame rate. The truth is there probably was some way to run it on a GeForce 256 but whether anyone would want to is another matter.

id Software’s next game RAGE would change course by being simultaneously developed and released for game consoles as well as PC by id themselves, with that generation of consoles being really the first generation where games could be delivered in rough feature parity with the PC.

In any event, it only makes sense that I try and port DOOM 3 next, however like I said there’s issues there too, namely do I do the original DOOM 3 port or the DOOM 3 BFG port. That latter entry on the PC is sort of redundant to some extent seeing as how various mod projects have taken the mantle of improving the graphics of the original game, but since it also shipped on the consoles of the era (360, PS3) it gave an opportunity for the game to be better represented on modern consoles.

The dilemma was further exacerbated by the fact that GOG only sells the BFG edition, not the vanilla one. You can still buy the regular one on Steam where it’s pretty much always $5 or less, whereas the BFG edition is $20 normally.

DOOM 3‘s source code was released in 2011, a few months after RAGE shipped. The following year the BFG edition was released and interestingly enough the source code for it was released the following month (I’m not an expert on this but I believe that they could have not released it under GPL since they could argue it was a derivative work of their original, older source and not a derivative work of the GPL-released code but they went ahead and just released the BFG edition’s code anyway).

In the Quake III: Arena world, the alpha and omega of source ports is ioquake3. I’m not completely clear on the specifics of why or how but basically the community coalesced around this one project. The name was derived from (the “io” in the name) so it naturally follows that a similar project would spring up for the DOOM 3 source code, and one did, iodoom3. However, unlike ioquake3, iodoom3 never really took off and stagnated to the point where, if you go to ioquake3’s website it looks like it’s the website of a modern game engine trying to sell you on its use, but iodoom3’s equivalent website tells you that you’re better off using one of two other projects: dhewm3 for the original source code, or RBDoom3BFG for the BFG release. Both of these are mature, well supported projects that otherwise have nothing to do with each other.

As a first step I figured I should at least get the original DOOM 3 running on the Mac in the form of the dhewm3 project. After sorting out a few things I was able to get the project building via a Makefile and soon enough I was up and running. It’s funny, a little while back I spotted a clearance copy of the BFG edition at a Walmart for the PS3 and since I still have my PS3 hooked up for watching movies, I figured I’d fire it up. In playing through it a bit and also playing through the game a bit on the Mac, I realize to some extent id Software chose to copy maybe the worst feature of Half-Life: it’s a really slow game at the start and it takes you a surprisingly large amount of time to get to the part you give a damn about. You go through this whole opening sequence with a slow moving spaceship and then you have to find this one dude, who sends you to this other dude, who sends you to follow this slow moving spider robot thing to go find this other dude, while at the same time there’s this conversation going on to the side which you get to see parts of, and then you get to the good part where everything goes to shit and then you get to start shooting things. The sequence where things go wrong and the people you just walked past become zombies is impressive and still works today, and the amount of time it takes to get to this point (including a spider robot you have to follow) is not bad when you’re strapped in for a journey, but it’s not a great drop-in-and-play game. They would rectify this spectacularly in DOOM 2016 by allowing you to shoot motherfuckers in like the first minute.

Like ioquake3, yquake2 and most well supported open source games, dhewm3 uses SDL, whereas the original game did not. As I’ve mentioned before, the Quake II port was the first one I’ve done where I used SDL’s iOS support. The Quake III: Arena port was based on Beben III which had gone out of its way to short circuit ioquake3’s SDL support, likely because the effort to get ioquake3 running on iOS predated the availability or viability of SDL on iOS. There were a number of hurdles to overcome with regards to getting one of these engines not only running on iOS with SDL, but also to fit in the CocoaTouch-based shell that I’ve been using. My hope was that after figuring this out I could get other games running quicker and more easily in iOS and tvOS. In the respect that this port took only a fraction of the time the Quake II port did, this wound up being true. But there’s a heck of a twist involved.

Having successfully compiled and run dhewm3 on the Mac via a makefile, I proceeded to try and get it running via an Xcode project. I’ve found that this whole process works best when you take baby steps. You know that the code will compile and run on the Mac, so you try and get it going in an Xcode project. When you run into issues… well you know there has to be some sort of fix because it compiles via the makefile fine. Then you get the code running in the Xcode project for iOS. When you run into issues… well you know it runs in the Xcode project on macOS so see what you may need to do different.

I ran into difficulty with the Xcode project for the macOS version but then I figured out something… the part of the process I didn’t mention yet is that it doesn’t just use a makefile, it uses CMake. CMake is sort of a meta layer above makefiles – when you point it to a directory with the CMake files it creates a makefile with all the stuff exactly like you need it for the platform you need and the location you want to compile the code.

I hate makefiles – I get why they use them and I even agree with why they use them, I just think they’re excruciatingly painful to work with. CMake files aren’t really much better. But CMake does do an extra thing I didn’t know at first (or had forgotten): you can use them to create Xcode projects (or Visual Studio projects, etc.). So I had CMake make me an Xcode project and that Xcode project built perfectly out of the box. I consulted what it does differently to make my Xcode project work and then I was up and running there, too.

So then it was time to actually start the iOS/tvOS project. I took the Quake2-iOS code, removed the actual Quake II engine code, commented out anything that relied on that code being there (like bridging hooks), and went in via BBEdit and replaced anything “Quake2” with “DOOM3”. I then verified that this built and ran despite having nothing to do. Weird having a DOOM 3 app on your phone with a Quake II icon.

Next, I did a wholesale import of the dhewm3 code. It’s a given that it won’t compile out of the box when you do this, so I wasn’t worried when exactly that happened. If nothing else, you have to change the imports of OpenGL include files with imports of OpenGL ES include files.

The next issue was with various other libraries. Coming from the Windows world, it took me a little bit to figure out the UNIX library naming convention is “lib___”. So for example one of the libraries needed was libzip (well, really it’s lzip so this isn’t the best example but go with it) which, like you might expect, is the library for zipping files, or reading the content of zip files. Easy enough. And you can add a reference to it right from the built in stuff in Xcode right out of the box.

Another thing dhewm3 needed (and this is probably the same for the base DOOM 3 source drop) is libjpeg which, unsurprisingly, is the library to read and write jpeg files. And I was thinking – well hell, there’s probably hundreds of those, right? Well maybe there’s more than one but pretty much everything pointed to the same libjpeg, and it’s literally the official library from the Joint Photographic Experts Group where they defined the jpeg standard.

Simple enough, but it’s not an option out of the box with Xcode, at least for iOS projects. Why? Well probably because on iOS they want you to, ideally, use something else like CoreGraphics, Apple’s library for handling graphics. Everything you can do with libjpeg can probably be done with CoreGraphics, though likely not with the same code.

Well, since going in and making everything in dhewm3 point to it’s CoreGraphics equivalent is beyond the scope of what I wanted to do, I instead just conjured up a libjpeg library for iOS.

A practice I saw in some early projects when dealing with iOS and Xcode was the use of embedded projects. Xcode has the concept of a workspace (.xcworkspace) and the concept of projects (.xcproj) and a workspace can have multiple parallel projects. However, a project can also have embedded projects of its own. I didn’t really like this practice and tended to lean towards workspaces or having dependencies with Carthage or something but a distinct advantage of the embedded project approach is that the project gets compiled, correctly, for whatever you’re targeting. When you run an app on the iOS Simulator, you’re running it on an x86_64 processor because that’s what the Mac is. But when you’re running on an actual device it’s an ARM processor. It’s possible to compile the binary in such a way that it basically combines the two but another way is just to link it in as a project and then Xcode just handles it for you. So I had libjpeg included as a project but for libcurl and the associated libraries it uses, I used the compiled versions I had from a previous project (I think it was the Quake III: Arena one).

Once I got past this I kept running into some issues with a C macro. dhewm3 does this thing where a bunch of code that effectively acts as a bridge to GL functions is expressed in the form of a call to a macro that expands them to the necessary functions instead of just including the functions directly. I’m not sure what the advantage is to this approach but basically I couldn’t tell if the issue was Xcode didn’t like the macro or if it was that these were referencing OpenGL concepts that didn’t exist in OpenGL ES, and Xcode, for all its strengths, kinda sucks at being clear or consistent at what is causing the issue.

But around this point I started to figure out that a lot of the remaining compilation errors were due to OpenGL related functionality. With the Quake II project, in the final equation the number of changes needed to bridge the gap between OpenGL and OpenGL ES was actually not that bad. But some searching seemed to indicate this wasn’t going to be the case with DOOM 3 at all. Basically, to pull off some of the stuff DOOM 3 did in 2004 when they delivered it, id had to pull some crazy shit that it appears didn’t make the cut for OpenGL ES, and implementing equivalents looked to be nontrivial.

Like I said, when I did the Quake II project, I found a fork of yquake2 that was targeting Android and so it had the OpenGL ES changes in place and I was able to graft them in. I went looking for something similar for dhewm3 and came up empty handed, but on a second pass it turns out I was too hasty to write off such a concept.

One of the forks of dhewm3 is called d3wasm. It’s a port of dhewm3 targeting WebAssembly which is something I’d never heard of before but I kinda thought that it was one of those deals where someone transcodes C++ to JavaScript to run in a web browser. I was wrong. I don’t really know how it works but it’s not that, although it does work in a web browser. And most importantly it uses OpenGL ES because some subset of that is what WebAssembly uses.

And fortunately the author of d3wasm likes to write long articles like I do so that was cool.

So I grafted over as much of the renderer code as seemed appropriate. A few things didn’t apply, like anything using Emsctipten (WebAssembly thing), but most stuff came over without a hitch and luckily there were few if any things in the code that pertained to changes elsewhere in his source.

After a couple of passes at grafting in code and working though some linker issues it compiled. Whenever this happens I’m thinking “holy shit”, but in this case it was especially crazy seeing as how I had been dicking with this for maybe a week, ten days tops. It took me over a month to get to this point with Quake II.

I had to figure out which main() function was effectively the real entry point (turns out it’s an SDL_Main() function) and modify that with conditional statements well enough to be the entry point from my CocoaTouch front end but once I did that I figured ok, let’s fire this puppy up and see how far he goes before he shits the bed.

And indeed there were a couple of “make the code look in the right place for the right data files” things and some tweaks to account for the whole thing being compiled into a glob instead of using libraries, but once I got that going not only did I see the video of the id Software logo, not only did I see the quick text of the console flying by, but I was sitting at the damn home screen of the game. And it worked.

That’s no moon. Seriously, it’s not a moon.

I rigged it up to get into a level instead of going to the home screen and suddenly I was in an elevator.

In less than two weeks of screwing with it, I’m suddenly running DOOM 3 on my iPhone. It’s because I had SDL scaffolding set up from another project and because someone else had done the OpenGL ES work but I still didn’t think I’d get this far this fast.

Not that I could go anywhere in the game mind you. The SDL scaffolding was recognizing my game controller like I expected but unlike in Quake II, it didn’t automatically translate to movement in the game. I’m not sure if this is because dhewm3 didn’t have that in place (it’s admittedly an unusual feature for an FPS on a PC) or what but it fell on me to figure our the right way to wire it up. But I had also not disabled the mouse pointer integration in SDL so I could look around a little bit. And tapping was like clicking the mouse button and letting go was like releasing it. Which, since for whatever reason spawning right into this map meant I could very easily throw a grenade which would bounce right off the door and blow me the hell up in this tin can of an elevator.

At this point it’s worth mentioning C++. Every single id game engine prior to DOOM 3 was written in C, not C++. In some ways, as near as I can tell, using C instead of C++ as long as id did was unusual, but it’s hard to argue with the results. Maybe the new hammer is better than the old one but when you’re really good with the old one it’s understandable to want to stick to it.

C was pretty much the original high level language and its syntactical choices (curly brackets, semicolons) would basically be the foundation of programming languages for decades. Sort of like how every fantasy setting for decades resembled Lord of the Rings in some way. Later came this concept of object oriented programming. C had variables and functions but no way to encapsulate an object with its own set of variables and functions. So various groups started to try and implement something like this, sometimes in code libraries, sometimes in new languages. My aforementioned Objective-C was one take on this, which is both why it has “object” in its name and also why it’s a supserset and reverse compatible with C. Another take was C++ which started out its life as “C with Classes”, and this is the one that really took hold in the cross platform application and game development communities. It’s not a complete superset of C (it’s possible to write C code that wouldn’t compile as C++ code, which is why there’s different extensions for .c and .cpp files)

In any event, it was interesting to see how id adapted to this. Most of their use of classes made logical sense and it was neat to see the evolution of it after messing with id Tech engines one at a time for about a year now. However it also meant that some of the tried and true “search for this function name” stuff that had semi-reliably worked for a while now no longer worked because the restructuring renamed things.

So once I figured out how to put basic movement in place and wire up the FIRE and JUMP buttons, I had it load up the regular game, right from the opening in-engine cutscene. And immediately I noticed problems.

The very first time you can control your character you’re on this dock thing and you need to climb some stairs and walk across a bridge to get to the indoor part of the game, except you can’t see the staircase.

And if you look at the ship it turns into this brown polygon.

Assuming you make it across the bridge in the near total darkness you get to a segment where a guy gives you your PDA, except it’s in near pitch black darkness too.

And the Martian surface is basically total darkness as well. Indoor segments mostly looked OK, although unless the lighting contractors do Broadway shows on the side, some of the lighting in there is ate up as well.

Thinking perhaps what I was experiencing was some of the “known issues” part of the d3wasm project, I fired up that version through a web browser and… nope, looks perfect there.

So then I tried it on Apple TV and noticed a couple of interesting things. First, the game ran poorly. Whereas the game is a near-constant 60fps on my iPhone X, it was averaging about 12fps on the Apple TV (4th generation, not the 5th generation “Apple TV 4K”, not that I’m sure that would make much of a difference). But the other interesting thing was: the graphic glitches didn’t occur on the Apple TV. I could see the staircase fine, I could see the dude giving me the PDA fine, the spotlight issue wasn’t on the floor, and I could see the surface of Mars. It did randomly crash at some point after everything went to hell in the game’s plot but the fact that the graphic issues weren’t there on a device years older than my phone was curious.

Something I’ve taken for granted up to this point is that modern device can run these old engines without breaking a sweat. Of course the $1,000 2017 iPhone can run the game engine designed for a 1999 PC. Even the 2001 game Return to Castle Wolfenstein was more or less running on the 1999 Quake III: Arena engine, and until DOOM 3 that was the most recent one. However, id Tech 4 is different.

When it was released in 2004, it had several detail modes. This isn’t new, id games have had this as far back as the original DOOM, what was unusual is that in 2004 the “Ultra Quality” mode could bring just about any machine to its knees. I’m not sure it was possible to build a PC in 2004 that could do that and if you could, it was going to be extremely expensive. I remember years later upgrading my video card and being elated that everything ran better and faster and then I tried DOOM 3 in “Ultra” mode and it still struggled with it.

In any event I’m not completely sure what the auto-detection criteria is but I think it’s possible that the games are running at Ultra quality which means it’s impressive that the iPhone can run them at 60fps and fit in your pocket, but it makes sense that the less beefy Apple TV would struggle with it.

I tried running it on my iPad Air 1 and it not only wouldn’t even finish loading up a game (ran out of memory for textures, I think) but it also crashed something about Xcode or macOS so hard that only a reboot would fix it, otherwise I couldn’t debug anything else.

I tried an iPad Air 2 and it ran there, and it was a similar deal to the Apple TV – lackluster frame rate, glitch-free visuals. So there was something about the newer iPhone X that caused the visuals to be screwed up that didn’t exist in older hardware.

I did a Beyond Compare with the logs of both the loading up of the iPhone X version and the iPad Air 2 version. And while things like the timings of how long it took to load things are pointless because of the differences in device speed, the one thing that really jumped out at me was that when the engine barks out OpenGL information, on the iPhone X it says this

OpenGL version: OpenGL ES 2.0 Metal 58.4

And on the iPad Air 2 it says this

OpenGL version: OpenGL ES 2.0 A8X GPU - 130.1

As a reminder, Metal is the name for Apple’s new 3D graphics API, pretty much their version of Direct3D in that it shines in performance on Apple hardware and is completely proprietary and non-portable, unlike OpenGL/OpenGL ES whose goal is to be portable. Apple has deprecated OpenGL and OpenGL ES and is telling game developers to start new projects on Metal if they can. OpenGL still exists on macOS and OpenGL ES still exists on iOS/tvOS and there’s no timetable for removing them but in all likelihood at some point they will just stop working. Apple is the same company that has changed the processor architecture three times on the Mac and cut off all 32-bit apps on iOS so they’re not afraid to say “fuck the past”, for better or worse.

All of that is to say that “OpenGL ES 2.0 Metal” makes about as much sense as “Jumbo Shrimp” in so far as those are the total opposite of each other. But my guess was – on the iPhone X, on iOS 12 (possibly earlier), OpenGL ES runs on top of Metal, possibly through an emulation layer. That might be one way to ensure that OpenGL ES games continue to run after OpenGL ES is removed from iOS. Possibly this is the reason it runs so well. However, it’s odd that even though both Apple TV and the iPad Air 2 are capable of running Metal, the same thing doesn’t exist for them. I posted the question on Stack Overflow and it was confirmed by someone there – OpenGL ES does run on top of Metal on the iPhone X and there’s no way around it.

So on a lark I wrote to the author of d3wasm and asked, since he had rewritten the entire renderer of DOOM 3 and I had only pasted in some files, did he have any advice on where I should start looking. He wrote me back and suggested a few things, like turning off lighting passes, tweaking a few console variables, etc. I did and while none of those presented clear solutions, I nevertheless started to understand a bit better how the rendering engine worked and how I could narrow down where the issue is occurring.

I had noticed a value calculated differently between the two devices, but consistent per-device, and I started to write a long email to the d3wasm author on what all I had tried and if he knew how this value was calculated when I had a rubber duck moment and realized the one thing I hadn’t tried to search for when looking up information on Stack Overflow and found this question on why WebGL GLSL scripts weren’t working on iOS when running through a Metal device like iPhone X – apparently there’s this line in GLSL scripts that specifies the floating point precision to be used in calculations. d3wasm, as well as the project mentioned in the question, specify low or medium precision, but for them to work in the Metal layer they have to be high precision.

Once I changed the precision to high, everything worked great and correctly on the iPhone X. Especially once I got the native resolution running as well.

I was elated. And in playing through some more, I came to realize that the places where I thought everything was working were not working as well as I had thought.

Note that as of right now I haven’t gone in and done the tweaking necessary to make the game dial down its detail level in order to run on the Apple TV at a framerate matching the iPhone. I will probably add that soon, possibly also adding the ability to change the detail level on the Options screen. All the “fun” of PC gaming now on tvOS.

Most of what came next was both typical and unique. Typical in that I had the usual things – like how I needed to get the physical and virtual controls working, and unique in that I had to wire things up a little differently as well as overcome some new things.

I added a crouch button since not only is crouching possible in this game but it’s required to progress in a few places. But this is the first id Software game I’ve ever done where you have to reload, and so that needed a button as well. Your weapon reloads if you try and fire it while empty but in some heated battles that’s not good enough.

Then there was the PDA. The PDA in DOOM 3 is probably the clearest sign someone played the System Shock games and wanted to emulate that. Early in the game you’re handed your PDA and it’s sort of hilarious since it’s this enormous thick tablet you’re… I guess carrying around? It’s good because it’s a way to add some extra depth to the game without being necessary if you don’t want to bother with it. At various places in the game you run into doors which don’t unlock unless you have the clearance from someone else’s PDA, which you usually find on desks or on corpses. Truth be told it’s probably entirely possible to play through and finish the entire game without ever touching the PDA, but there’s other things in there like lore or emails from people which are everything from story beats to fake spam to codes saying what the combination to some safe is so you can get more gear, ammo, etc. Truth be told it’s pretty neat, though the lore parts and their optionality would be in DOOM 2016 as well, just without the need for some literal in-game device.

The thing is when you’re in the PDA you need to be able to maneuver with a mouse. I’m actually not sure how the console ports handle this, but the way I had the game set up the controller wasn’t handling the mouse pointer, and I’m not sure you’d want to either. I mean you’re on a touchscreen device using a controller to move a cursor on a screen that’s emulating… a touchscreen device. Really ideally I’d have the taps you make on your phone screen just match the PDA’s cursor but I wasn’t able to figure that out in time.

I started to go down figuring out how to emulate the mouse pointer with drags of your finger but then I remembered SDL by default actually maps the mouse to your finger dragging and touching the screen to mouse clicks. It’s the reason I could turn around in the elevator with the grenade and toss the grenade but not actually go anywhere. I had disabled this but re-enabling it when you’re in the PDA was a quick and easy way to get the basic effect I wanted.

Really I re-enabled the dragging part but I disabled the actual mouse “clicks” unless you’re in the PDA since the mouse “clicks” otherwise would be firing your gun in the regular game and maybe you want to fine adjust your viewpoint but not actually fire your weapon. For this to be effective though I reduced the size of the virtual “button” to change your weapon – you can still tap your weapon and switch it but it’s a smaller hit area, and the top part of the screen now no longer goes through them either. I also put an invisible button in the bottom right of the screen to call or dismiss the PDA. When you get something new for your PDA this is the part of the screen that shows an icon so this made sense to me.

This wound up coming in very handy for other in-game screens – at various places you have to interact with touch screens and in the regular PC version of the game this winds up causing you to lower your weapon and now your crosshairs are a mouse pointer. The problem I had there was that you instinctively want to tap once you’ve moved the cursor in place but you’re in the game, not the PDA, so it doesn’t work. And if there’s a foolproof way to do this I couldn’t figure it out but it mostly works. If you find yourself unable to stop firing just open and close the PDA until I figure out how to fix this. But you can also just press the fire button or trigger and do the same thing. A lot of the screens are basically “eye level” so you don’t really need to tilt up or down to use them but sometimes you do.

The flashlight I went so far as to repurpose what was the F1 button in Quake II to be an actual flashlight icon. The flashlight is a regular weapon you can get to by scrolling the mouse wheel but you frequently need to switch right to it or right back so they implemented a button for that – I mapped it to the flashlight icon and on a MFi controller, it’s the left trigger. It still has the “can’t have both at the same time” issue like before but it’s at least sort of intuitive with this quick switch icon/button.

Most of the rest of the work was just on the cosmetics – like menus, colors icons, etc. Since this isn’t a Quake game the Quake font is pretty much out, so instead I used this font commonly associated with the Diablo games. It may seem weird to use that font but it’s what they used on marketing materials and the making of book


The last cosmetic thing was pretty much the icons. One of the things I’ve liked about making these ports of the DOOM games is that the “DOOM” logo, with the whole angled letters and so forth, pretty much never changes. It changes appearance but it’s pretty much the same basic shape, so you can make the logos to where each one just takes over the position of the other.

However for the iPhone icons I had made the DOOM logo take up as much of the icon as possible, but since they did the “3” in DOOM 3 as a superscript number (like the Alien 3 logo, I guess?) this means the “DOOM” part of that icon would be smaller and wouldn’t match the other icons. And I’m an OCD weirdo so that bothered me.


So I went to the trouble of making the other DOOM app logos have the same sized “DOOM” logo on them. I originally made each of the logos centered but the “3” off to one side and just negative space on the left just looked dumb so I just offset the DOOM 3 logos. I had also added this kinda cool background to the expansion pack icon and the regular icon looked kinda bland with just a black background so I added a stock photo of Mars to it. But now I see the cool background of the expansion pack icon is barely noticeable but whatever I’ve already spent too much time on this

We’re a happy family

So anyway, this project went so quickly and the resulting game is so relatively involved that I feel like I’m forgetting something, so I don’t know, maybe watch this space for updates. I even had most of this article written a few weeks ago and I probably could have had it done sooner but for some real life responsibilities.

I only have a video of the iPhone version for now because I want to get the performance on Apple TV better before capturing that.

If anyone wants to contact me I can be reached at

Schnapple’s Adventures in id Tech

Wolfenstein 3-D
iOS | tvOS
iOS | tvOS

iOS | tvOS
iOS | tvOS
source | article
Final DOOM
source | article

source | article
iOS | tvOS
Quake II
source | article
iOS | tvOS

Quake III: Arena
source | article
iOS | tvOS
Return to Castle

source | article
iOS | tvOS

source | article
video: iOS