Why I’m starting this project#

To make it short: nostalgy.

When people talk about the games that make them nostalgic, I thought there would be a good part of The Legend of Zelda and Pokémon games. After asking a few friends what the games that make them nostalgic were, I got a huge variety of answers: Left 4 Dead, GTA San Andreas, Tomb Raider, Fable, some Star Wars games, SSB, Age of Empire, Warcraft 3, Quake 3, etc.

For me, I spent way too much time on Call of Duty 1 & 2, and Need for Speed Most Wanted. I must have finished them at least 3 times each, plus a good number of hours on the multiplayer of both Call of Duty (abbr. CoD). These are the games that make me nostalgic.

The goal of this project is to be able to explore the CoD 1 maps directly from the browser, like any spectator mod. Maybe I will try to extend it to CoD 2 in the future, but I would rather try to finish it entirely for CoD 1 first. I have to be honest on two things:

  1. I’m a total newbie on 3D graphics and reverse-engineering games, so this project helped me to learn a lot in different domains.
  2. The idea of exploring old game maps from a browser is not mine. I have to give kudos to the devs of noclip.website for this, who inspired me to work on this project. Unfortunately they don’t have any Call of Duty games on it, so I’ll have to start my research on how to do that elsewhere.

How CoD assets work#

To make this projects, I’ll need two kinds of assets from my CoD installation: the maps and the textures. Both of these assets are situated in the main/*.pk3 files, which are just zip files with a different extension. After I unzipped each of these files, I found 2 folders which will help me: maps and textures.

Maps#

Since CoD 1 uses the id Tech 3 engine, its maps are .bsp files (more info here), and a decompiler is needed to extract the data I need. After some research, I found this tool which does just what I need: decompile a binary .bsp file into a text .map file, which is way easier to use. For the moment, I’m using their already decompiled maps, but this seems like a good way to go to write my own BSP decompiler in the future. I already did a little bit of research and found an Unofficial Quake 3 Map Specifications.

In this engine, a map is composed of entities. In CoD 1, I noticed that the entities are either a set of triangles, a set of brushes (a brush defines a polyhedron, more on that later), or specific coordinates on the map (I haven’t looked at this in details yet…).

Map format differences#

Of course, to make it easy, there are different versions of the MAP format, but they should contain the same information either way. I’m currently using the format used in the already decompiled maps I got, but I found that it doesn’t really follow the Quake Map Format.

For example, the information about a specific triangle will be shown as this in the version I’m using:

 {
  patchTerrainDef3
  {
   normandy/ground/dirt@earthbasenoise_lpa
   ( 2 2 0 0 0 15 4 )
(
( ( 496 -1726 -2 -65.5975 -215.301 64 51 62 0 0 ) ( 496 -1726 -2 -65.5975 -215.301 64 51 62 0 0 ) )
( ( 368 -1726 -2 -20.7893 -259.73 64 51 62 0 0 ) ( 440 -1784 -48 20.5078 -371.504 0 44 62 0 0 ) )
)
  }
 }

While the official Quake Map Format will require the same triangle to be like this:

 {
  mesh
  {
layer "Triangles"
   toolFlags splitGeo;
   textures/normandy/ground/dirt@earthbasenoise_lpa
   lightmap_gray
   2 2 16 8
   (
    v 496 -1726 -2 t -215.301 998.959
    v 496 -1726 -2 t -215.301 998.959
   )
   (
    v 368 -1726 -2 t -259.73 990.295
    v 440 -1784 -48 t -371.504 954.013
   )
  }
 }

For the moment, there are only 4 pieces of informations I’m using for the triangle: the 3 corners (there are actually 4 but one of the corners is doubled since the polygons needs to be closed), and the texture applied to the triangle.
I haven’t yet looked at the brushes.

Textures#

For now, it seems that all textures are stored in the textures folder, a mix of .dds, .jpg, and .tga formats. When loading the textures, I can’t know in advance which format of image I’ll need to load (and therefore which URL I need to reach), so I made a small python script to help me deal with this.

Python script

It returns a JSON file containing an object and for each name of texture used in the .map file, there’s the corresponding URL path:

{
    "normandy/ground/dirt@earthbase1noise_lowcon": "/assets/textures/normandy/ground/dirt@earthbase1noise_lowcon.dds",
    "normandy/ground/dirt@earthbasenoise_lpa": "/assets/textures/normandy/ground/dirt@earthbasenoise_lpa.dds",
    ...
    "normandy/ground/gravel@rubbletranstee": "/assets/textures/normandy/ground/gravel@rubbletranstee.tga",
    ...
}

Building the map in 3D#

Like I said before, I don’t know anything in 3D graphics, but I knew that I wanted to do it in a webpage. So I googled “js 3d library” and found three.js.

The first step was to parse the MAP file to fetch the different triangles and add them to a ThreeJS scene.

Parsing the map#

I decided to work first on the triangles since it represents how the map is made (ground, walls, sky, etc.).
After parsing the file for the first time, I initially had trouble to display properly the textures since I didn’t really know how ThreeJS worked. So I ended up copying a tutorial to display each triangle with different hues and gradients.

Watch how it looked like at first

The most expert eyes will recognize that this map is “mp_carentan” (I didn’t realize that the X axis was in fact inverted at this point). With the parsing of triangles done, I really wanted to apply textures to it, so I decided to work on the “Brushes” later.

Applying textures#

After a few hours of looking at a lot of tutorials and trying to use textures, I managed to have several triangles with different textures on each:

Once I got this, I managed to put the right textures on each of the triangles and finally have something that looked kind of correct :

There is still a few problems at this point:

  • Some textures are missing (I haven’t located the sky texture yet, among other ones).
  • The textures are not in the right orientation. I think the other parameters in the MAP file should help me fix this.
  • The textures don’t have an uniform scaling. This is due to something called UVs to place textures onto a polygon: http://paulyg.f2s.com/uv.htm.

Next improvements#

So, there is still a long way to go to even properly display one whole map properly. The next steps I will work on will be:

  1. Try to make the texture apply correctly (scaling, orientation, distortion) to each of the triangles.
  2. Some triangles displaying decals are on the same plane as the wall they are one, so they do not display properly.
  3. Since I’ve only worked on the triangles for now, all I have are the ground and the buildings, as I haven’t looked into parsing the Brushes in the MAP file yet.
  4. Fix the mouse & keyboard controls.
  5. Parse the original BSP file myself.
  6. Add a menu to select any map from CoD 1.
  7. Try to port this to CoD 2.