MAP (file format): Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (added to techinical category)
No edit summary
 
(70 intermediate revisions by 6 users not shown)
Line 1: Line 1:
The MAP format is a widely used format, and the one accepted by the compilers. It's also a text format, meaning that it's easy for people to mess around with - but they can only do this if they know what everything does. Previous articles on the subject refrained from going into the gory details in many parts - especially texturing. This article aims to rectify that, and provide the full and complete MAP format for all to see.
{{LanguageBar}}
{{tabsBar|main=MAP}}
{{non-valve engine|engine|This page covers all major variants of the MAP file format, beyond that which is used by {{gldsrc|2}}, to provide a comprehensive overview of the file format as a whole and explain why compatibility isn't trivial.}}
{{stub}}
The '''MAP''' file format is a plain-text file format used by {{gldsrc|4.1}}, {{idtech2|4.1}}, {{idtech3|4.1}}, and {{idtech4|4.1}}, which stores entities and level geometry in a format which can be understood by the map compilers (and in the case of {{idtech4|3.1}}, the game itself). {{src|4.1|addtext='s}} [[VBSP]] also has residual support for compiling from MAP files.


What people often get confused about is that a Half-Life map (or any Quake engine-based map, for that matter) is entirely entities. However, for simplicity, the main world geometry is often referred to as not being an entity; this is, technically, wrong. Anything that isn't tied to an entity is part of the entity called "worldspawn" - remember this.
At its core, the format consists of several [[entities]], each of which is optionally capable of storing one or more [[brush]]es, {{only|{{idtech3}}{{idtech4}}}} [[#Patch meshes|patch meshes]], and {{src|only}} [[#Displacements|displacements]].


Why'd I just tell you that? Well, the MAP format is organised into entities. Everything is defined by entities - including the world, in the "worldspawn" entity. Let's take a look at how a basic entity is defined:
{{warning|Compatibility isn't trivial. GoldSrc compilers exclusively use the Quake 1 Valve220 format, but MAP files exist in a multitude of variants; see [[#Variants]].
}}
== Variants ==
{| class="wikitable"
|-
! Header text !! Supported by !! Information
|-
| {{Quake|4|nt=0}} (Legacy) || Most tools, except {{hammer4|4.1}} || The original format.
|-
| {{Quake|4|nt=0}} (Valve220) || All {{gldsrc|4.1}} tools, most modern {{quakeengine|4.1}} tools, and {{hammer4|4.1}} || Variant of the Quake 1 (Legacy) format with additional texture UV precision.<br>Introduced by {{worldcraft|4.1|addtext={{nbsp}}2.0}}; now standard.
|-
| {{Quake2|4}} (Legacy) || All {{quake2engine|4}} tools, and most {{idtech3|4}} tools || Variant of Quake 1 (Legacy) format which optionally stores three additional [[WAL]] metadata flags.
|-
| {{Quake2|4}} (Valve220) || {{jack|4}}, {{nrc|4}}, and {{trenchbroom|4}} || Variant of Quake 2 (Legacy) format with Valve220-style UVs.<br>Introduced by {{jack|4.1}}; now standard.
|-
| {{Quake3|4}} (Axial Projection) || All {{idtech3|4}} tools
| rowspan=2 | Variant of Quake 2 (Legacy) format with support for patch meshes (analogous to [[displacement]]s, but use Bézier curves instead of an array of vertices).


<pre>
While Quake 3 MAP files can technically store both standard brushes and [[#Brush primitives|brush primitives]], map editors usually require selecting one or the other to write the file as.
{
 
    "classname" "entity_classname_here"
The Quake 3 MAP format still supports the metadata flags from Quake 2, including on patch meshes and brush primitives, although only the detail flag ({{mono|0x08000000}}) is ordinarily used; this information is instead usually stored in the [[material]] definitions.
    "property1" "value1"
|-
    "property2" "value2"
| {{Quake3|4}} (Brush Primitives) || Most {{idtech3|4}} tools, except {{q3r|4}} and {{trenchbroom|4}}
    "propertyn" "valuen"
|-
}
| {{Quake3|4}} (Valve220) || {{jack|4}}, {{nrc|4}}, {{trenchbroom|4}} || Variant of Quake 3 format with Valve220-style UVs.<br>Introduced by {{jack|4.1}}; not yet standard.
</pre>
|-
| {{Doom3|4}} (Axial Projection) || All {{idtech4|4}} tools
| rowspan=2 | Variant of Quake 3 format which can store patch meshes and brush primitives differently {{elaborate}}.


"Classname" is the name for the special property that defines what entity it actually is; it could be assigned a value of "info_player_start", "func_breakable", "func_wall", or others (like "worldspawn", which we'll talk about later). After that come the list of properties for an entity. To explain this, it's easier to give an example - here's an entry for a light:
Like Quake 3, a map can technically store both standard brushes and brush primitives, but editors write the files as one or the other. Standard brushes are generally considered deprecated in favor of brush primitives.
|-
| {{Doom3|4}} (Brush Primitives) || Most {{idtech4|4}} tools, except '''TrenchBroomBFG'''
|-
| {{doom3|4}} (Valve220) || '''TrenchBroomBFG''' || Variant of Doom 3 format with Valve220-style UVs for standard brushes.<br>Introduced by [https://github.com/RobertBeckebans/TrenchBroomBFG TrenchbroomBFG]; not standard.
|-
| {{src|4}} (Valve400) || None || Variant of Quake I Valve220 format with support for [[displacement]]s. Early versions of Source (~2001-2002?) used this before [[VMF]]; stripped from [[VBSP]] by the time Source released.
|-
| {{w|Doom Eternal}} || '''[https://idstudio.idsoftware.com/ idStudio]''' || Variant of Doom 3 brush primitives format with additional metadata ([https://github.com/jmarshall23/Doom3ToDoomEternalConverter Converter]).
|}


== Entities ==
{{stub}}
=== KeyValues ===
== Geometry ==
=== Brushes ===
In most versions of the format, brushes are stored as a set of ''[[plane]]s'', rather than a set of [[vertex|vertices]]. Each plane is defined via three points, and the intersection of these planes determines where edges and vertices lay. Due to this, brushes cannot be concave, and a missing face will result in other faces continuing infinitely, causing compiler errors.
==== Quake I ====
A simple 6-sided brush in the original Quake 1 format would look like this:
<pre>
<pre>
{
{
     "classname" "light"
     ( -16 -64 -16 ) ( -16 -63 -16 ) ( -16 -64 -15 ) mmetal1_2 -0 -0 -0 1 1
     "_light" "255 255 128 200"
    ( -64 -16 -16 ) ( -64 -16 -15 ) ( -63 -16 -16 ) mmetal1_2 -0 -0 -0 1 1
     "origin" "128 -256 128"
    ( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) mmetal1_2 0 0 0 1 1
     ( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) mmetal1_2 0 0 0 1 1
     ( 64 16 16 ) ( 65 16 16 ) ( 64 16 17 ) mmetal1_2 -0 -0 -0 1 1
    ( 16 64 16 ) ( 16 64 17 ) ( 16 65 16 ) mmetal1_2 -0 -0 -0 1 1
}
}
</pre>
</pre>


That's all there is for a point entity, but when it comes to a brush entity, more is required. As brush entities are just that - brushes tied to an entity - the brushes need to be defined. This is done in a way which will likely be alien to most people; using the intersection of a collection of planes. Here's a pseudo brush entity:
Each plane can be summed up like so:
 
<pre>
<pre>
{
( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURENAME Uoffset Voffset rotation Uscale Vscale
    "classname" "entity_classname_here"
    "property1" "value1"
    "property2" "value2"
    "propertyn" "valuen"
    {
        ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURE_NAME [ tx1 ty1 tz1 toffs1 ] [ tx2 ty2 tz2 toffs2 ] rotation scaleX scaleY
        ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURE_NAME [ tx1 ty1 tz1 toffs1 ] [ tx2 ty2 tz2 toffs2 ] rotation scaleX scaleY
        ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURE_NAME [ tx1 ty1 tz1 toffs1 ] [ tx2 ty2 tz2 toffs2 ] rotation scaleX scaleY
        ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURE_NAME [ tx1 ty1 tz1 toffs1 ] [ tx2 ty2 tz2 toffs2 ] rotation scaleX scaleY
        ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURE_NAME [ tx1 ty1 tz1 toffs1 ] [ tx2 ty2 tz2 toffs2 ] rotation scaleX scaleY
        ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURE_NAME [ tx1 ty1 tz1 toffs1 ] [ tx2 ty2 tz2 toffs2 ] rotation scaleX scaleY
    }
}
</pre>
</pre>


(These lines are split into two by the width of the Collective's pages. In a file, a line from ( x1 y1 z1 ) right through to scaleY would reside on the same line of text. When talking about one line, I'll be referring to one line in an actual file, not how it appears on the Collective's pages.)
* {{code|( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 )}} is the set of three points in 3D space that define the plane.
* {{code|TEXTURENAME}} is the name of the [[texture]] (or in later engines, the [[material]]) that appears on the face. This is case insensitive, and never includes a file extension.
* {{code|Uoffset Voffset}} offsets the UV coordinates of the plane, resulting in shifting the texture on the U and/or V axis. These are projected onto the face using axial projection.
* {{code|rotation}} stores the rotation of the texture.
* {{code|Uscale Vscale}} stores the texture scale. 1 means the texture is displayed at 1 texel per quake unit.


The basic definition of the entity is the same, but after defining its properties comes a collection of other stuff. It's this that defines the brushes that make up the entity. Every pair of curly braces within the curly braces of the entity itself defines one brush belonging to the entity. If you had more than one brush, you'd have another load of defining planes inside a pair of curly braces after the first load.
{{note|The original Quake I compilers only supported integer coordinates, but most forks and all subsequent compilers support floating point coordinates in addition to integer coordinates. Nonetheless, it is preferable to only store brushes using floating point when necessary, to avoid floating point precision loss errors. This is less important for point entities, which cause fewer issues when off-grid.}}


Let's examine exactly how brushes get defined. (When talking about a 'line', I'll be referring to the lines of text in the definition of a brush.) Every line defines one plane (a plane is an infinitely thin, infinitely big 'sheet' in 3D space) which, essentially, defines one face. To get the actual faces of the brush, the area contained within all the planes put together is used.
==== Valve220 ====
 
The original Quake MAP format uses axial projection to apply textures onto faces. This results in undesirably stretched textures on slanted surfaces. To rectify this, Valve came up with the Valve220 format, which stores the texture offsets as 1-dimensional arrays, instead of singular numbers, as so:
To define one actual plane, three points must be given (shown by x1, y1, z1, x2, y2, z2 and x3, y3, z3). They must be in a clockwise order when facing the outside of the plane - that is, the side that points outwards from the brush, and these points must lie on the surface of the plane. They must also be different from one another, and not lie on a line - they must form three corners of a triangle when joined up. (Three vertices of the face being represented by this plane will often suffice.)
<pre>
 
( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURENAME [ Ux Uy Uz Uoffset ] [ Vx Vy Vz Voffset ] rotation Uscale Vscale
After those three points, it's all defining how the face residing on that plane will be textured. First up is fairly self-explanatory - the texture name - but the next part is fairly difficult to both explain and to grasp. To define the orientation of textures, a pair of axes are used. These define what is "flat" for that face's texture space. If you picture a flat plane covered in the texture, with the two axes lying flat on that plane, that's the texture's plane. This is then flattened right onto the plane of the face. This means that if you want the texture to be perfect scale on the face, the axes need to define a plane that is parallel to the face's plane, otherwise you'll get stretching.
</pre>
* The first three values of each texture offset array are the normal vectors of the U and V axes.
The texture plane (on the right) 'flattened' onto the actual face (left) - notice how it gets stretched on the face.
* The third value is the offset itself, like the axial projection format.
 
{{modernImportant|In Valve220 projection, the {{code|rotation}} value should match the orientation of the UV normal vectors. Not doing so will result in editor bugs, compiler errors, and/or invalid brushes!}}
These axes are defined by tx1/y1/z1 and tx2/y2/z2, giving two vectors which define what's "right" and what's "up" relative to the texture - so if you wanted to go "right" 1 unit, you'd go along the first axis for 1 unit. Going "down" 2 units would be going backwards along the second axis for 2 units. How much the texture is offset along these axes is defined by toffs1 and toffs2, measured in HL units.
 
The last three items are fairly simple, but rotation is a fairly weird one. Contrary to what you may think, it doesn't define how much the texture should be rotated; it defines how much it ALREADY HAS been rotated. This is so Worldcraft/Hammer can rotate the texture axes accordingly, and means that if you wanted to code a program to rotate the texture 45 degrees, you'd need to add 45 to rotation (so that WC/Hammer shows it being rotated 45) and rotate the two texture axes by 45 degrees too.
 
Luckily, scaleX and scaleY are both very simple; they define how much the texture is stretched along the two texture axes. Operation is the same as WC/Hammer - the larger the value, the more it's streched. Less than 1, and it's squeezed up; less than 0, and it's mirrored.
 
Having shown you all that, here's an example of a simple brush entity - a func_breakable:


A simple 6-sided brush in the Quake 1 format using Valve220 projection would look like this:
<pre>
<pre>
{
{
     "classname" "func_breakable"
     ( -16 -64 -16 ) ( -16 -63 -16 ) ( -16 -64 -15 ) mmetal1_2 [ 0 -1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    "spawnflags" "256"
    ( -64 -16 -16 ) ( -64 -16 -15 ) ( -63 -16 -16 ) mmetal1_2 [ 1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    "rendercolor" "0 0 0"
    ( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) mmetal1_2 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
    "health" "100"
    ( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) mmetal1_2 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
    "material" "3"
    ( 64 16 16 ) ( 65 16 16 ) ( 64 16 17 ) mmetal1_2 [ -1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    "explosion" "1"
    ( 16 64 16 ) ( 16 64 17 ) ( 16 65 16 ) mmetal1_2 [ 0 1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    {
        ( 256 -192 192 ) ( 384 -192 192 ) ( 384 -320 192 ) AAATRIGGER [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1  
        ( 256 -320 64 ) ( 384 -320 64 ) ( 384 -192 64 ) AAATRIGGER [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1  
        ( 256 -192 192 ) ( 256 -320 192 ) ( 256 -320 64 ) AAATRIGGER [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1  
        ( 384 -192 64 ) ( 384 -320 64 ) ( 384 -320 192 ) AAATRIGGER [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1  
        ( 384 -192 192 ) ( 256 -192 192 ) ( 256 -192 64 ) AAATRIGGER [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1  
        ( 384 -320 64 ) ( 256 -320 64 ) ( 256 -320 192 ) AAATRIGGER [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1  
    }
}
}
</pre>
</pre>


There's only one brush in it, a cube.  
The Valve220 format was introduced for Half-Life, and is required for GoldSrc compilers. Since the BSP format used by all IdTech engine games store UV coordinates that are at least as accurate as Valve220 (and sometimes moreso), the Valve220 format has mostly supplanted the legacy axial projection format by mappers, as many modern compilers for IdTech engines support Valve220.


There's one property I haven't mentioned yet, and that's "spawnflags". You won't see it in the normal list of keys and properties when looking at an entity's properties box in Hammer, as it represents which flags are ticked. Every flag is assigned a value, which is always a power of two - the first flag will be 1 (20), the second will be 2 (21), the third 4 (22) and so on. If a flag is on, this value is added to the running total - if it isn't, then no value is added. The total of all flags that're on is assigned to the "spawnflags" property of the entity.
The {{ent|worldspawn}} [[keyvalue]] pair {{code|"mapversion" "220"}} indicates that a MAP file uses Valve220 texture projection. Map converters and decompilers that output to Valve220 should add the KV if it is not already present.


This is all the knowledge that's required to understand the MAP format, but as I said earlier, I will elaborate on worldspawn. Essentially, it's exactly the same as other brush entities (its properties can be edited by going to the Map menu and selecting Map properties in Hammer) except with a few extra properties that should be defined. Here's an example of a "worldspawn" entity, defining the world geometry (in this case, just a huge cube) and other information:
==== Quake II ====
Quake II's format optionally allows for three additional values to be stored, as so:
<pre>
( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) path/texturename Uoffset Voffset rotation Uscale Vscale SurfaceFlag ContentsFlag Value
</pre>
These represent the same values as in a [[WAL]] file, and are stored as Base 10 integers (unlike in code, where they are usually declared in hexadecimal form). These values are optional, however. If not present, the compilers will use the values stored in the WALs themselves.
{{note|Due to Quake II storing its textures as separate files (usually packed inside a [[PAK]]) instead of combined into [[WAD]]s, the path to the texture is stored. The Quake I map format technically supports storing the path, but this isn't used due to to WADs not having subdirectories.}}
A brush in the original Quake II format could look like this:  
{{todo|Insert example here}}


{{jack|4}} introduced a modified version of the format which uses Valve220 notation, as such:
<pre>
<pre>
{
( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) path/texturename [ Ux Uy Uz Uoffset ] [ Vx Vy Vz Voffset ] rotation Uscale Vscale SurfaceFlag ContentsFlag Value
    "classname" "worldspawn"
    "sounds" "1"
    "MaxRange" "4096"
    "mapversion" "220"
    "wad" "\half-life\valve\xeno.wad;\half-life\valve\decals.wad;\half-life\valve\halflife.wad;\half-life\valve\liquids.wad"
    {
        ( 0 0 0 ) ( 512 0 0 ) ( 512 -512 0 ) AAATRIGGER [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
        ( 0 -512 -512 ) ( 512 -512 -512 ) ( 512 0 -512 ) AAATRIGGER [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
        ( 0 0 0 ) ( 0 -512 0 ) ( 0 -512 -512 ) AAATRIGGER [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
        ( 512 0 -512 ) ( 512 -512 -512 ) ( 512 -512 0 ) AAATRIGGER [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
        ( 512 0 0 ) ( 0 0 0 ) ( 0 0 -512 ) AAATRIGGER [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
        ( 512 -512 -512 ) ( 0 -512 -512 ) ( 0 -512 0 ) AAATRIGGER [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
    }
}
</pre>
</pre>
A brush in this modified version of the format could therefore look like this:
{{todo|inset example here}}
Most modern editors (ex: {{jack}}{{trenchbroom}}{{nrc}}) and compilers ([[ericw-tools]], [https://cdn.discordapp.com/attachments/360596986392739840/1140802047181070508/kmqbsp_114_test10.zip KMBSP 1.14], [https://github.com/qbism/q2tools-220 q2tools220]) support this modified format.
Since the Quake II MAP format is largely unchanged from the Quake I format, it can be converted to the older format without losing any geometry by simply stripping the extra flags. This can be done either with [[ericw-tools]] ({{code|maputil.exe --strip_extended_info input.map}}) or by opening the file in {{jack|4.1}} using a game profile configured for the Quake I/Hexen II or Half-Life/TFC "Map Type". It is not necessary to do any conversions to go from Quake I to Quake II.
{{note|Due to Quake II textures being stored as separate files in folders, it will be necessary to batch remove the preceding path from the texture string if porting to Quake I or GoldSrc. Additionally, any textures with names longer than 15 characters will need to be renamed.<br>If using the Quake I format as an intermediary for porting to Source, these additional steps are not necessary.}}
==== Brush primitives ====
{{gtkr|4}} and its forks allow storing brushes as "brush primitives" ({{mono|brushdef}}) instead of standard brushes. This method allows for more control over brush texture alignment than standard brushes. Additionally, unlike standard brushes, the texture UVs of brush primitives are normalized to a 0 to 1 range, so texture scale is independent of texture resolution.
A map can be converted between standard brushes and brush primitives using maputil from [[ericw-tools]]. To convert to brush primitives:
: {{codeblock|maputil.exe --convert bp input.map}}
To convert to regular brushes, using Valve220 texture projection:
: {{codeblock|maputil.exe --convert valve input.map}}
Note that conversion requires the tools to be able to access the textures to properly convert UV coordinates; if maputil cannot find a given texture, it will assume a 64x64 resolution. If a map makes use of textures from [[PK3]] files or materials from {{mono|.shader}} files that don't match the name of a texture, it may be preferable to use {{nrc|4}} to convert the map instead{{how}}, to preserve proper UV coordinates.


The 4 properties there are all required - especially the "wad" property. This defines what WADs the map will require, relative to the directory above the one that hl.exe resides in, separated by semicolons.
=== Patch meshes ===
{{stub}}
Patch meshes are stored in the MAP file as a set of {{w|Bézier curve|Bézier curves}}, which dictate how the mesh is automatically tessellated at runtime.


And there you have it - the MAP format in a nutshell. Hopefully you can now see why concave brushes are impossible to represent in this format without creating two or more separate brushes, and you can now have fun creating programs to manipulate MAP files to your heart's content.
=== Displacements ===
{{stub}}
MAP files can store {{src|nt=0|2}} [[displacements]]{{how}}, as [[VBSP]] still supports for compiling MAP files. The "mapversion" keyvalue should be 400. Some forks of Q3Map2 also support compiling these displacements into {{quake3|2}} BSPs.


== See also ==
* [[VERC/MAP file format]] - casual explanation of the Quake 1 Valve220 MAP format
* [[VMF]]
* [[RMF]]


'''Orignally posted by Francis 'DeathWish' Woodhouse on the VERC.'''
== External links ==
* {{quakewiki|Quake Map Format}}
* [https://quark.sourceforge.io/infobase/src.topics.html Quark Documentation] - Discusses the way the MAP file format stores brush faces and Bézier curves


[[Category:Technical]]
[[Category:File formats]]

Latest revision as of 02:13, 27 June 2025

English (en)中文 (zh)Translate (Translate)
edit
Info icon
This page documents information about an engine not developed by Valve. This information is documented here because it has technical and/or historical relevance to Valve's engines.
This page covers all major variants of the MAP file format, beyond that which is used by GoldSrc GoldSrc, to provide a comprehensive overview of the file format as a whole and explain why compatibility isn't trivial.

Stub

This article or section is a stub. You can help by expanding it.

The MAP file format is a plain-text file format used by GoldSrc GoldSrc, id Tech 2 id Tech 2, id Tech 3 id Tech 3, and id Tech 4 id Tech 4, which stores entities and level geometry in a format which can be understood by the map compilers (and in the case of id Tech 4, the game itself). Source Source's VBSP also has residual support for compiling from MAP files.

At its core, the format consists of several entities, each of which is optionally capable of storing one or more brushes, (only in id Tech 3id Tech 4) patch meshes, and (only in Source) displacements.

Warning.pngWarning:Compatibility isn't trivial. GoldSrc compilers exclusively use the Quake 1 Valve220 format, but MAP files exist in a multitude of variants; see #Variants.

Variants

Header text Supported by Information
Quake Quake I (Legacy) Most tools, except Hammer Hammer 4.x The original format.
Quake Quake I (Valve220) All GoldSrc GoldSrc tools, most modern Quake Engine Quake Engine tools, and Hammer Hammer 4.x Variant of the Quake 1 (Legacy) format with additional texture UV precision.
Introduced by Hammer 3.x Worldcraft 2.0; now standard.
Quake II Quake II (Legacy) All Quake II Engine Quake II Engine tools, and most id Tech 3 id Tech 3 tools Variant of Quake 1 (Legacy) format which optionally stores three additional WAL metadata flags.
Quake II Quake II (Valve220) J.A.C.K. J.A.C.K., NetRadiant-Custom NetRadiant-Custom, and TrenchBroom TrenchBroom Variant of Quake 2 (Legacy) format with Valve220-style UVs.
Introduced by J.A.C.K. J.A.C.K.; now standard.
Quake III Quake III (Axial Projection) All id Tech 3 id Tech 3 tools Variant of Quake 2 (Legacy) format with support for patch meshes (analogous to displacements, but use Bézier curves instead of an array of vertices).

While Quake 3 MAP files can technically store both standard brushes and brush primitives, map editors usually require selecting one or the other to write the file as.

The Quake 3 MAP format still supports the metadata flags from Quake 2, including on patch meshes and brush primitives, although only the detail flag (0x08000000) is ordinarily used; this information is instead usually stored in the material definitions.

Quake III Quake III (Brush Primitives) Most id Tech 3 id Tech 3 tools, except Q3Radiant Q3Radiant and TrenchBroom TrenchBroom
Quake III Quake III (Valve220) J.A.C.K. J.A.C.K., NetRadiant-Custom NetRadiant-Custom, TrenchBroom TrenchBroom Variant of Quake 3 format with Valve220-style UVs.
Introduced by J.A.C.K. J.A.C.K.; not yet standard.
Doom 3 Doom 3 (Axial Projection) All id Tech 4 id Tech 4 tools Variant of Quake 3 format which can store patch meshes and brush primitives differently [Elaborate?].

Like Quake 3, a map can technically store both standard brushes and brush primitives, but editors write the files as one or the other. Standard brushes are generally considered deprecated in favor of brush primitives.

Doom 3 Doom 3 (Brush Primitives) Most id Tech 4 id Tech 4 tools, except TrenchBroomBFG
Doom 3 Doom 3 (Valve220) TrenchBroomBFG Variant of Doom 3 format with Valve220-style UVs for standard brushes.
Introduced by TrenchbroomBFG; not standard.
Source Source (Valve400) None Variant of Quake I Valve220 format with support for displacements. Early versions of Source (~2001-2002?) used this before VMF; stripped from VBSP by the time Source released.
Wikipedia icon Doom Eternal idStudio Variant of Doom 3 brush primitives format with additional metadata (Converter).

Entities

Stub

This article or section is a stub. You can help by expanding it.

KeyValues

Geometry

Brushes

In most versions of the format, brushes are stored as a set of planes, rather than a set of vertices. Each plane is defined via three points, and the intersection of these planes determines where edges and vertices lay. Due to this, brushes cannot be concave, and a missing face will result in other faces continuing infinitely, causing compiler errors.

Quake I

A simple 6-sided brush in the original Quake 1 format would look like this:

{
    ( -16 -64 -16 ) ( -16 -63 -16 ) ( -16 -64 -15 ) mmetal1_2 -0 -0 -0 1 1
    ( -64 -16 -16 ) ( -64 -16 -15 ) ( -63 -16 -16 ) mmetal1_2 -0 -0 -0 1 1
    ( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) mmetal1_2 0 0 0 1 1
    ( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) mmetal1_2 0 0 0 1 1
    ( 64 16 16 ) ( 65 16 16 ) ( 64 16 17 ) mmetal1_2 -0 -0 -0 1 1
    ( 16 64 16 ) ( 16 64 17 ) ( 16 65 16 ) mmetal1_2 -0 -0 -0 1 1
}

Each plane can be summed up like so:

( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURENAME Uoffset Voffset rotation Uscale Vscale
  • ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) is the set of three points in 3D space that define the plane.
  • TEXTURENAME is the name of the texture (or in later engines, the material) that appears on the face. This is case insensitive, and never includes a file extension.
  • Uoffset Voffset offsets the UV coordinates of the plane, resulting in shifting the texture on the U and/or V axis. These are projected onto the face using axial projection.
  • rotation stores the rotation of the texture.
  • Uscale Vscale stores the texture scale. 1 means the texture is displayed at 1 texel per quake unit.
Note.pngNote:The original Quake I compilers only supported integer coordinates, but most forks and all subsequent compilers support floating point coordinates in addition to integer coordinates. Nonetheless, it is preferable to only store brushes using floating point when necessary, to avoid floating point precision loss errors. This is less important for point entities, which cause fewer issues when off-grid.

Valve220

The original Quake MAP format uses axial projection to apply textures onto faces. This results in undesirably stretched textures on slanted surfaces. To rectify this, Valve came up with the Valve220 format, which stores the texture offsets as 1-dimensional arrays, instead of singular numbers, as so:

( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURENAME [ Ux Uy Uz Uoffset ] [ Vx Vy Vz Voffset ] rotation Uscale Vscale
  • The first three values of each texture offset array are the normal vectors of the U and V axes.
  • The third value is the offset itself, like the axial projection format.
Icon-Important.pngImportant:In Valve220 projection, the rotation value should match the orientation of the UV normal vectors. Not doing so will result in editor bugs, compiler errors, and/or invalid brushes!

A simple 6-sided brush in the Quake 1 format using Valve220 projection would look like this:

{
    ( -16 -64 -16 ) ( -16 -63 -16 ) ( -16 -64 -15 ) mmetal1_2 [ 0 -1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    ( -64 -16 -16 ) ( -64 -16 -15 ) ( -63 -16 -16 ) mmetal1_2 [ 1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    ( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) mmetal1_2 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
    ( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) mmetal1_2 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
    ( 64 16 16 ) ( 65 16 16 ) ( 64 16 17 ) mmetal1_2 [ -1 0 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
    ( 16 64 16 ) ( 16 64 17 ) ( 16 65 16 ) mmetal1_2 [ 0 1 0 -0 ] [ 0 0 -1 -0 ] -0 1 1
}

The Valve220 format was introduced for Half-Life, and is required for GoldSrc compilers. Since the BSP format used by all IdTech engine games store UV coordinates that are at least as accurate as Valve220 (and sometimes moreso), the Valve220 format has mostly supplanted the legacy axial projection format by mappers, as many modern compilers for IdTech engines support Valve220.

The worldspawn keyvalue pair "mapversion" "220" indicates that a MAP file uses Valve220 texture projection. Map converters and decompilers that output to Valve220 should add the KV if it is not already present.

Quake II

Quake II's format optionally allows for three additional values to be stored, as so:

( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) path/texturename Uoffset Voffset rotation Uscale Vscale SurfaceFlag ContentsFlag Value

These represent the same values as in a WAL file, and are stored as Base 10 integers (unlike in code, where they are usually declared in hexadecimal form). These values are optional, however. If not present, the compilers will use the values stored in the WALs themselves.

Note.pngNote:Due to Quake II storing its textures as separate files (usually packed inside a PAK) instead of combined into WADs, the path to the texture is stored. The Quake I map format technically supports storing the path, but this isn't used due to to WADs not having subdirectories.

A brush in the original Quake II format could look like this:

Todo: Insert example here

J.A.C.K. J.A.C.K. introduced a modified version of the format which uses Valve220 notation, as such:

( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) path/texturename [ Ux Uy Uz Uoffset ] [ Vx Vy Vz Voffset ] rotation Uscale Vscale SurfaceFlag ContentsFlag Value

A brush in this modified version of the format could therefore look like this:

Todo: inset example here

Most modern editors (ex: J.A.C.K.TrenchBroomNetRadiant-Custom) and compilers (ericw-tools, KMBSP 1.14, q2tools220) support this modified format.

Since the Quake II MAP format is largely unchanged from the Quake I format, it can be converted to the older format without losing any geometry by simply stripping the extra flags. This can be done either with ericw-tools (maputil.exe --strip_extended_info input.map) or by opening the file in J.A.C.K. J.A.C.K. using a game profile configured for the Quake I/Hexen II or Half-Life/TFC "Map Type". It is not necessary to do any conversions to go from Quake I to Quake II.

Note.pngNote:Due to Quake II textures being stored as separate files in folders, it will be necessary to batch remove the preceding path from the texture string if porting to Quake I or GoldSrc. Additionally, any textures with names longer than 15 characters will need to be renamed.
If using the Quake I format as an intermediary for porting to Source, these additional steps are not necessary.

Brush primitives

GtkRadiant GtkRadiant and its forks allow storing brushes as "brush primitives" (brushdef) instead of standard brushes. This method allows for more control over brush texture alignment than standard brushes. Additionally, unlike standard brushes, the texture UVs of brush primitives are normalized to a 0 to 1 range, so texture scale is independent of texture resolution.

A map can be converted between standard brushes and brush primitives using maputil from ericw-tools. To convert to brush primitives:

maputil.exe --convert bp input.map

To convert to regular brushes, using Valve220 texture projection:

maputil.exe --convert valve input.map

Note that conversion requires the tools to be able to access the textures to properly convert UV coordinates; if maputil cannot find a given texture, it will assume a 64x64 resolution. If a map makes use of textures from PK3 files or materials from .shader files that don't match the name of a texture, it may be preferable to use NetRadiant-Custom NetRadiant-Custom to convert the map instead[How?], to preserve proper UV coordinates.

Patch meshes

Stub

This article or section is a stub. You can help by expanding it.

Patch meshes are stored in the MAP file as a set of Wikipedia icon Bézier curves, which dictate how the mesh is automatically tessellated at runtime.

Displacements

Stub

This article or section is a stub. You can help by expanding it.

MAP files can store Source Source 1 displacements[How?], as VBSP still supports for compiling MAP files. The "mapversion" keyvalue should be 400. Some forks of Q3Map2 also support compiling these displacements into Quake III Quake III BSPs.

See also

External links