April 5, 2015

Handling Quake 1 .map Files (Part 1)

Now, let's say you want to handle Quake 1 .map files.  Perhaps you want to do the project I'm doing: porting Quake to other engines.  Perhaps you want to use Quake 1-compatible level editors for your MonoGame or three.js projects.  Who knows?  Code for this series will be provided in C# for easy integration into tools and content pipelines.

First, let's look at what an entry in a .map file looks like.

  ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURENAME xofs yofs rotation xscale yscale
 "key" "value" // Optional comment

Each entry in a .map file is enclosed in curly braces to encompass an entity.

Each entity can contain zero to many brushes.  Each brush is enclosed in curly braces.

Each brush must be convex can contain four to many faces.  (Why four?  Think of a three-sided pyramid and the base.)

Each face includes three vertices to define the plane, shown above as (x1,y1,z1) through (x3,y3,z3), a texture name, and all the information needed to calculate the texture coordinates for each final vertex.  Vertices are in clockwise order.  Most of the time, these vertices are the first three vertices of that face, but not always.  They're just there to define the plane.

Each entity can also contain one to many key/value pairs.  Each entity will have one key named "classname" which tells you what other key/value pairs you should expect.

If you find a classname of "worldspawn" in the entity, this will be all of your static geometry.  You can use this information to heavily optimize this information for rendering or other work: do texture batching, visibility calculations, collision calculations, etc.

While you can have comments in your .map files, you probably won't see them.

If you think about it, this gives you a good way to handle save game separation as well.  When you are saving your game, iterate through your world and serialize out the information for any entity that isn't worldspawn.  When you are loading the saved game, you can load only the worldspawn entity, then overlay your other entity information from your save game.

Now that we have that information, we know what we need to parse the .map file.  Here's a functioning map parser.  It fills a Map object with Entities that can contain key/value pairs and Brush objects containing Faces.


Next time, we'll turn this information to a polygon soup.