# MAP file format

{% hint style="warning" %}
**This is a technical file format specification article for programmers.**&#x20;

For mapping tutorials, see [Quake resources](https://book.leveldesignbook.com/appendix/resources/quake) instead.
{% endhint %}

The .MAP file format is a 3D file format devised for Quake 1 (1996).&#x20;

It is gradually emerging as the standard file type for brush-based levels in any game engine, partly because it is relatively simple to parse and edit. It's similar to how .OBJ has become a simple 3D standard too, even though it lacks many advanced features.

## Parsers

There's a good chance someone has already written a generic .MAP parser for your game engine / programming language.

* C++ (<https://github.com/stefanha/map-files>)
* C# (<https://github.com/Figglewatts/MapParse> or <https://github.com/LogicAndTrick/sledge-formats>)
* Rust (<https://github.com/reslario/nomap> or <https://github.com/QodotPlugin/quarchitect>)

&#x20;   *For game engine-specific MAP importers and integrations, see* [*TrenchBroom*](https://book.leveldesignbook.com/appendix/tools/trenchbroom)*.*

## File format

* Text based with curly braces and line breaks
* Z axis is up (Quake convention), right-handed
* `angle` values are Euler degrees from 0-359
  * -1 means "up"
  * -2 means "down"
  * some Quake mods implement rotations as a 3D vector called `m_angle`

### Entities

Every object in a .MAP must be an **entity**, a generic actor / game object container type.

There are two types of entities:

* **point entities:** monsters, items, lights, things with fixed size / no size
* **brush entities:** point entities with 3D shapes embedded inside them
  * *worldspawn:* global static brush entity, the "root" of the entire game world
  * world brushes: brushes bound to worldspawn

Entity data and templates are defined by a [**.FGD** file](https://book.leveldesignbook.com/appendix/resources/formats/fgd) loaded into the level editor.

{% code title="Entity schema" %}

```javascript
{
   // classname: the type of entity
   "classname" "(ENTITY TYPE)"
   
   // moreentity data goes here
   
   {
      // (optional) brush data goes here
   }
}
```

{% endcode %}

{% code title="Point entity" %}

```javascript
// a Point Entity is a curly brace scope
// with keyvalue pairs surrounded by quotation marks
{
    // classname: the type of entity
    "classname" "info_player_start"
    
    // spawnflags: a bitmask of toggles for the entity
    "spawnflags" "0"
    
    // origin: the entity's position in 3D space
    "origin" "32 32 24"
    
    // but this is just Quake convention, you could put any arbitrary data here
}
```

{% endcode %}

{% code title="Brush entity" %}

```java
// a Brush Entity is like a Point Entity but with 3D brush definitions inside
{
    "spawnflags" "0"
    
    // remember: worldspawn is the traditional root entity of all world brushes
    "classname" "worldspawn"
    // note that 'classname' is often NOT the first keyvalue pair, don't assume!
    
    // a file path to a Quake WAD (texture set)
    "wad" "E:\q1maps\Q.wad"
    {
        // 3D brush geometry information, see next section for details
        ( -16 -64 -16 ) ( -16 -63 -16 ) ( -16 -64 -15 ) __TB_empty [ 0 -1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
        ( -64 -16 -16 ) ( -64 -16 -15 ) ( -63 -16 -16 ) __TB_empty [ 1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
        ( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) __TB_empty [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
        ( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) __TB_empty [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
        ( 64 16 16 ) ( 65 16 16 ) ( 64 16 17 ) __TB_empty [ -1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
        ( 16 64 16 ) ( 16 64 17 ) ( 16 65 16 ) __TB_empty [ 0 1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    }
}
```

{% endcode %}

### Brush geometry

A **brush** is a convex 3D shape defined by 4 or more planes.&#x20;

**It is NOT a traditional 3D mesh format made of vertices and triangles.** You need an additional map compile step to process the brush data into a usable 3D mesh. For example, Quake maps use an editor-time command line tool called QBSP that outputs a compiled .BSP map file; meanwhile many .MAP parsers for other engines instead generate this mesh at runtime.

Every brush line defines one 3D plane (a triangle) and its texture coordinates:

{% code title="Brush plane" %}

```mathml
(x1 y1 z1) (x2 y2 z2) (x3 y3 z3) TEXTURE_NAME [ ux uy uz offsetX ] [ vx vy vz offsetY ] rotation scaleX scaleY
```

{% endcode %}

Building the 3D mesh based on plane intersections is a bit complicated. Most libraries implement some variant on [Stefan Hajnoczi's 2001 C++ implementation](https://github.com/stefanha/map-files). His paper explains the math and process in detail, PDF download mirrored below:

{% file src="<https://1666186240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LtVT8pJjInrrHVCovzy%2Fuploads%2FEukkFYJLwfafFXUMpsI2%2FMAPFiles_2001_StefanHajnoczi.pdf?alt=media&token=51471685-bf69-42ae-a015-a474c0b95165>" %}
uploaded from <https://github.com/stefanha/map-files> on 23 March 2022
{% endfile %}

#### Texturing / UVs

The Quake .MAP file format has two versions: the original Standard version, and an updated format by Valve Software.&#x20;

The original Standard format had problems storing texture rotations. Given a 3D brush plane normal in XYZ, it discards the longest axis and stores the resulting Vector2. But if the plane is at a 45 degree angle, then the longest axis is now ambiguous and the map compiler must now arbitrarily favor an axis; [floating point imprecision](https://timothybramlett.com/floating-point-imprecision.html) makes this preference even less predictable, often resulting in unpleasant texture stretching.&#x20;

Standard texture coordinates:

`TEXTURE_NAME offsetX offsetY rotation scaleX scaleY`

Meanwhile, more recent Valve format .MAPs have the `"mapversion" "220"` key value set in the worldspawn. Valve format 220 resolves the 45 degree ambiguity case by storing UVs in Vector3 space, which also enables some useful UV skewing operations in [TrenchBroom](https://book.leveldesignbook.com/appendix/tools/trenchbroom) as well.&#x20;

Valve format texture coordinates:

`TEXTURE_NAME [ ux uy uz offsetX ] [ vx vy vz offsetY ] rotation scaleX scaleY`

**We generally recommend using Valve format**, and there's no reason to use standard format other than backwards compatibility / porting old files to the updated format.

{% code title="ValveFormatTexParse.c" lineNumbers="true" %}

```c
// in the Valve texture format, parsing the file would yield these values in order:
// texname [ ux uy uz offsetX ] [ vx vy vz offsetY ] rotation scaleX scaleY
// NOTE: Valve format doesn't actually use the "rotation" value anymore

// per vertex and per face, coordinates can be calculated like this

Vec3 axisU = Vec3(ux, uy, uz) / scaleX;
Vec3 axisV = Vec3(vx, vy, vz) / scaleY;

for (FaceVertex& v : Vertices)
{
   // Dot product of (v.Point, axisU)
   v.TexU = v.Point.x * axisU.x + v.Point.y * axisU.y + v.Point.z * axisU.z;
   // Dot product of (v.Point, axisV)
   v.TexV = v.Point.x * axisV.x + v.Point.y * axisV.y + v.Point.z * axisV.z;
   v.TexU += offsetX;
   v.TexV += offsetY;
   v.TexU /= Texture->GetWidth();
   v.TexV /= Texture->GetHeight();
}
```

{% endcode %}

## Example .MAP file

&#x20;   *For much more complex example .MAP files, see* [*Quake resources*](https://book.leveldesignbook.com/appendix/resources/quake)*.*

{% code title="SimpleExampleMap.map" lineNumbers="true" %}

```javascript
// entity 0
{
    "spawnflags" "0"
    "classname" "worldspawn"
    "wad" "E:\q1maps\Q.wad"
    // brush 0
    {
        ( -16 -64 -16 ) ( -16 -63 -16 ) ( -16 -64 -15 ) __TB_empty [ 0 -1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
        ( -64 -16 -16 ) ( -64 -16 -15 ) ( -63 -16 -16 ) __TB_empty [ 1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
        ( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) __TB_empty [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
        ( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) __TB_empty [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
        ( 64 16 16 ) ( 65 16 16 ) ( 64 16 17 ) __TB_empty [ -1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
        ( 16 64 16 ) ( 16 64 17 ) ( 16 65 16 ) __TB_empty [ 0 1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    }
}

// entity 1
{
    "spawnflags" "0"
    "classname" "info_player_start"
    "origin" "32 32 24"
}
```

{% endcode %}

## Sources

* <https://quakewiki.org/wiki/Quake_Map_Format>
* <https://www.quakewiki.net/archives/qref/wcs/info.html>
* [Quake MAP Specs (1996) via Internet Archive](https://web.archive.org/web/20220515161805/https://www.gamers.org/dEngine/quake/QDP/qmapspec.html)
