Формат Source Engine BSP
Вступление
Этот документ описывает структуру файлового формата BSP, используемого в Half-Life 2 и других модификациях, построенных на этом движке. Это достаточно простой формат, который немногим отличается от BSP формата Half-Life, который базируется на форматах Quake 1 и Quake 2. Вам возможно будет интересно почитать статью от Max McGuire под названием «Quake 2 BSP File Format», здесь описаны основы всей структуры, то, с чего все начиналось.
Сведения о формате BSP использовались при создании декомпилятора карт Half-Life 2 — VMEX. Он умеет конвертировать BSP файлы обратно, в формат VMF, пригодном для открытия в редакторе Hammer.
Большая часть информации в этом документе заимствована из статьи Max McGuire, а так же прямо из исходных кодов, которые включены в состав Source SDK (например заголовочный файл public/bspfile.h), а так же при самостоятельных экспериментах с написанием VMEX. Это вовсе не официальный документ, но он отражает официальную спецификацию от Valve. Любые поправки или дополнительная информация по этому поводу только приветствуется.
Файлы формата BSP различаются между собой по версиям. Этот документ описывает 19 версию формата, которая используется в HL2, HL2: DM, CS: S. Игра Vampire: The Masquerade Bloodlines использует более старую и измененную версию, версию 17; данный формат немного отличается от 19 версии, ситуация еще осложняется тем, что SDK для VTMB не доступен вообще, поэтому все приходится изучать самостоятельно.

Обзор
Формат BSP содержит всю необходимую информацию, чтобы движок Source правильно отобразил карту и дал возможность играть на ней. Сюда входят: геометрия, т. е. все полигоны на уровне; имена и направления всех текстур, которые будут отображаться на полигонах; данные, необходимые для симуляции физики, а так же иные предметы окружения; расположение и свойства всех brush-based, model (prop) based и неотображаемых (логических) entities на карте; все BSP древо и таблица видимости, которые используются, чтобы установить нахождение игрока на карте внутри геометрии и эффективно отобразить карту на экран. Карта может нести в себе так же текстуры и даже модели, используемые на уровне, которые располагаются внутри Pakfile lump (смотри ниже).
Стоит помнить, что не вся информация может содержаться в карте, например файл BSP не включает в себя описание карты (которое можно увидеть при загрузке в CS: S или HL2: DM) и навигацию для AI, для этого создаются отдельные файлы (имя_карты.txt и имя_карты.nav соответственно). Source Engine сам подгружает дополнительные файлы и файлы из Pakfile lump во время загрузки карты.
Официальные карты находятся в Steam Game Cache File (GCF) формате и доступ к ним осуществляется через файловую систему Steam. Чтобы извлечь файлы из GCF файла, нужно применить программу Nemesisґ GCF Scape.

Заголовок BSP файла
BSP файл начинается с заголовка. Эта структура идентифицирует файл как Valve Source Engine BSP файл, содержит версию формата, содержит длину и версию всех подсекций файла, содержит информацию о различных частях карты.
Структура BSP файла была взята из SDK (public/bspfile.h), заголовок имеет длину в 1036 байт и определяется как:
struct dheader_t
{
int ident; // BSP file identifier

int version; // BSP file version
lump_t lumps[HEADER_LUMPS]; // lump directory array
int mapRevision; // the map`s revision (iteration, version) number
};

Здесь ident — это 4-х байтовая сигнатура, которая определяется как:
IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'V') //little-endian 'VBSP';

Как вы уже догадались, получилась аббревиатура «VBSP». Именно она идентифицирует Valve BSP файл; другие BSP форматы используют другие сигнатуры (например iD Software в Quake engine использовала «IBSP»). BSP формат HL1 вообще не использовал сигнатур.
Второй Integer определяет версию BSP файла (BSPVERSION); для HL2 на данный момент это 19 (в десятичной системе счисления); VMTB использует формат версии 17.
Замечание: BSP файлы для других игр используют собственные диапазоны номеров версий.
Далее следует 16-байтовая lump_t структура. Константа HEADER_LUMPS имеет значение 64, т. е. создается 64 элемента, причем на данный момент используется только 48.
Вот как выглядит структура lump_t, объявленная в bspfile.h:
struct lump_t
{
int fileofs; // offset into file (bytes)
int filelen; // length of lump (bytes)
int version; // lump format version
char fourCC[4]; // lump ident code
};

Первые две целочисленные переменные содержат смещение в байтах (по отношению к началу BSP файла) и размер в байтах lump-блока данных; далее следует версия формата текущего lump-блока и 4-байтовый идентификатор, который практически всегда имеет значение 0, 0, 0, 0. Неиспользуемые элементы массива lumps содержат переменные, равные нулю.
Тип данных, который содержится в массиве lumps, зависит от позиции в этом массиве, например: за первым lump в массиве (Lump 0) всегда закреплены BSP entity-данные (смотри ниже). Истинное местоположение данных в BSP определяется исключительно по смещению и размеру конкретного lump-блока, и не зависит от порядка в массиве, например: данные об entity сохраняются в конце файла, в то время как lump-блок является первым в массиве.
Список всех lump в массиве (неизвестные помечаются знаком вопроса):
Lump: Name: Purpose:
0 Entities Map entities
1 Planes Plane array
2 Texdata Index to texture names
3 Vertexes Vertex array
4 Visibility Compressed visibility bit arrays
5 Nodes BSP tree nodes
6 Texinfo Face texture array
7 Faces Face array
8 Lighting Lightmap array
9 Occlusion Occlusion data (?)
10 Leafs BSP tree leaf nodes
11 Unused
12 Edges Edge array
13 Surfedges Index of edges
14 Models Brush models (geometry of brush entities)
15 Worldlights Light entities
16 LeafFaces Index to faces in each leaf
17 LeafBrushes Index to brushes in each leaf
18 Brushes Brush array
19 Brushsides Brushside array
20 Areas Area array
21 AreaPortals Portals between areas
22 Portals Polygons defining the boundary between adjacent leaves (?)
23 Clusters Leaves that are enterable by the player
24 PortalVerts Vertices of portal polygons
25 Clusterportals Polygons defining the boundary between adjacent clusters (?)
26 Dispinfo Displacement surface array
27 OriginalFaces Brush faces array before BSP splitting
28 Unused
29 PhysCollide Physics collision data (?)
30 VertNormals Vertex normals (?)
31 VertNormalIndicies Vertex normal index array (?)
32 DispLightmapAlphas Displacement lightmap data (?)
33 DispVerts Vertices of displacement surface meshes
34 DispLightmapSamplePos Displacement lightmap data (?)
35 GameLump Game-specific data lump
36 LeafWaterData (?)
37 Primitives (?)
38 PrimVerts (?)
39 PrimIndices (?)
40 Pakfile Embedded uncompressed-Zip format file
41 ClipPortalVerts (?)
42 Cubemaps Env_cubemap location array
43 TexdataStringData Texture name data
44 TexdataStringTable Index array into texdata string data
45 Overlays Info_overlay array
46 LeafMinDistToWater (?)
47 FaceMacroTextureInfo (?)
48 DispTris Displacement surface triangles
49 PhysCollideSurface Physics collision surface data (?)
50−64 Unused

Структура некоторых lump-данных будет описана ниже.
В конце заголовка содержится номер проверки, который основывается на номере проверки в исходном vmf-файле, вдобавок к нему добавляется время, когда карта была сохранена.

Plane Lump
Основой геометрии BSP являются planes, они служат совмещенными поверхностями в структуре BSP древа.
Plane lump (1) — это указатель на структуру dplane_t:
struct dplane_t
{
Vector normal; // normal vector
float dist; // distance from origin
int type; // plane axis identifier
};

Где Vector — это всего лишь структура, представляющая из себя систему координат в 3-D пространстве:
struct Vector
{
float x;
float y;
float z;
};

Plane определяется елементом normal, normal vector, с размерами одного юнита (1.0) и перпендикулярно к поверхности plane. Позиция самого plane определяется через dist — это дистанция от центра карты (0,0,0) до plane.
Математически, plane рассчитывается как определенный набор точек (x, y, z) и является равенством:
F (x, y, z) = Ax + By + Cz + D

Где A, B и C — это компоненты normal.x, normal.y и normal.z, D — это dist.
Переменная type содержит определенный флаг, сигнализирующий о том, что Plane перпендикулярен к координатной оси, обычно он не используется.
Максимальное количество plane на вашей карте — 65536 (MAX_MAP_PLANES).

Vertex Lump
Vertex lump (3) содержим массив всех vertexґов (углов) в геометрии карты. Угол представляет собой простую точку с 3-мя координатами (x, y, z).
Максимальное количество углов — 65536 (MAX_MAP_VERTS).

Edge Lump
Edge lump (12) — это указатель на структуру dedge_t:
struct dedge_t
{
unsigned short v[2]; // vertex indices
};

Edge — ребро — это линия, соединяющая два угла. Максимальное количество ребер — 256000 (MAX_MAP_EDGES).

Face и Original Face Lumps
Face lump (7) содержит основную геометрию карты, используемую игровым движком, для отображения картинки на экране.
Face lump — это одна или несколько комплексных структур в файле карты. Это массив из элементов dface_t, длиной в 56 байт.

struct dface_t
{
unsigned short planenum; // the plane number
byte side; // faces opposite to the node`s plane direction
byte onNode; // 1 of on node, 0 if in leaf
int firstedge; // index into surfedges
short numedges; // number of surfedges
short texinfo; // texture info
short dispinfo; // displacement info
short surfaceFogVolumeID; //?
byte styles[4]; // switchable lighting info
int lightofs; // offset into lightmap lump
float area; // face area in units2
int LightmapTextureMinsInLuxels[2]; // texture lighting info
int LightmapTextureSizeInLuxels[2]; // texture lighting info
int origFace; // original face this was split from
unsigned short numPrims; // primitives
unsigned short firstPrimID;
unsigned int smoothingGroups; // lightmap smoothing group
};


Версия BSP 17 содержит немного иную, модифицированную структуру.
struct dface_bsp17_t
{
byte unknown[32];
unsigned short planenum;
byte side;
byte onNode;
int firstedge;
short numedges;
short texinfo;
short dispinfo;
byte unknown[50];
int origFace;
unsigned int smoothingGroups;
};


Brush и Brushside Lumps
Brush lump (18) содержит все браши, которые содержались в оригинальном VMF файле (фактически они не требуют декомпиляции, создать ни их основе декомпилированную карту — простая задача). Этот lump содержит 12-и байтную dbrush_t структуру

struct dbrush_t
{
int firstside; // first brushside
int numsides; // number of brushsides
int contents; // contents flags
};


Особо здесь стоит отметить переменную contents, вот таблица принимаемых ей значений:

CONTENTS_EMPTY 0 // No contents
CONTENTS_SOLID 0×1 // an eye is never valid in a solid
CONTENTS_WINDOW 0×2 // translucent, but not watery (glass)
CONTENTS_AUX 0x4
CONTENTS_GRATE 0×8 // alpha-tested «grate» textures. Bullets/sight pass through, but solids don’t
CONTENTS_SLIME 0×10
CONTENTS_WATER 0×20
CONTENTS_MIST 0×40
CONTENTS_OPAQUE 0×80 // things that cannot be seen through (may be non-solid though)
CONTENTS_TESTFOGVOLUME 0×100 // can see into a fogvolume (water)
CONTENTS_MOVEABLE 0×4000
CONTENTS_AREAPORTAL 0×8000
CONTENTS_PLAYERCLIP 0×10000
CONTENTS_MONSTERCLIP 0×20000
CONTENTS_CURRENT_0 0×40000
CONTENTS_CURRENT_90 0×80000
CONTENTS_CURRENT_180 0×100000
CONTENTS_CURRENT_270 0×200000
CONTENTS_CURRENT_UP 0×400000
CONTENTS_CURRENT_DOWN 0×800000
CONTENTS_ORIGIN 0×1000000 // removed before bsping an entity
CONTENTS_MONSTER 0×2000000 // should never be on a brush, only in game
CONTENTS_DEBRIS 0×4000000
CONTENTS_DETAIL 0×8000000 // brushes to be added after vis leafs
CONTENTS_TRANSLUCENT 0×10000000 // auto set if any surface has trans
CONTENTS_LADDER 0×20000000
CONTENTS_HITBOX 0×40000000 // use accurate hitboxes on trace


Максимальное количество брашей — 8192 (MAX_MAP_BRUSHES)

Brushside lump (19) — массив 8-байтной структуры:
struct dbrushside_t
{
unsigned short planenum; // facing out of the leaf
short texinfo; // texture info
short dispinfo; // displacement info
short bevel; // is the side a bevel plane?
};


Максимальное количество brushside — 65536 (MAX_MAP_BRUSHSIDES). Максимальное количество brushside на одном браше — 128 (MAX_BRUSH_SIDES).


PS. От переводчика: Данный перевод является неполным, вследствие огромной нехватки времени, а статья чрезвычайно полезна, поэтому я спешу выложить ее на сайт. Если вы хотите узнать больше о формате BSP, читайте оригинал, написанный самим автором VMEXґа. Главным справочником вам должен служить файл bspfile.h, несомненно, он служит источником самой полной и достоверной информации. Используя эти данные вы сможете без проблем написать компилятор/декомпилятор карт для Half-Life 2 и ее модификаций, все в ваших руках, удачи в постижении внутреннего мира Source Engine.
Перевод: VDm.
17 июня 2005, 10:10