May 23th, 2013

Doom3 BFG Source Code Review: DooM Classic (Part 4 of 4) >>

Doom 3 BFG allows us to play Doom 1 and Doom 2 :) ! From a programmer perspective, it doesn't seem too complicated to integrate Doom Classic engine inside Doom3. A straight-forward approach would be to provide abstraction layers for:

But Doom 3 BFG is not that simple in the sense that it allows split-screen multiplayers. Something the original idTech1 was never intended to perform.

Doom Classic Architecture

Doom Classic uses a standard design: An infinite loop, interacting with sub-systems where each subsystems is using local or common global variables as follow :

The multiplayer experience was aimed at LANs (Internet was not very widely adopted in the early 90s). There was no client/server: Each player ran its own game simulation and had to wait for inputs from each players before rendering a frame :

    while (gameOn)
        ReadIncomingIPXPackets();          // Receive network Inputs
        if (allPlayersInputsReceived())
           break;                          // The engine can only proceed when a command for each player has been received.
      DrawScreen();                        // The local simulation state can be drawn to screen.

The Challenge

So the architectural challenge is : How do you run several instance of a game with no client/server and many global variables on a single machine ?

A naive approach would be to host 4 Doom engines (loading several times a DLL). Doom3 could them route inputs to the proper instance and also retrieve the framebuffer for outputs.

An other approach would be to go over the code of Doom 1 and duplicate each state information so there is one for each player:

How does Doom III runs Doom Classic ?

Doom 3 BFG ended up doing something rather elegant: They refactored idTech1 into a state machine which involved modifying 100% of the old codebase :

Higher resolution

It is not immediately apparent because of some old assets (such a the weapon) but Doom Classic is running at three times the resolution (from 320x200 to 960x600). The rendition is still performed with the old school software renderer:

DooM I/O

Type Implementation details
Video The Doom 1 renderer writes pixels to a Doom 3 texture that is uploaded to the GPU at the end of each frame via idRenderSystemLocal::UploadImage.
Sound Direct calls to XAudio2, the low-level audio API.
Player Inputs Joystick inputs are converted to events and feed a brand new Events System (D_PostEvent,D_ProcessEvents) on a ring buffer.
Filesystem Abstracted via Doom3 idFileSystem.



Fabien Sanglard @2013