ZenkakuHiragana / SourceBSPFormatJP

Source BSP File Formatを日本語訳したもの(Google 翻訳 + 分かりやすく)

Home Page:https://www.patreon.com/ZenkakuHiragana

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SourceBSPFormatJP

Valve Developer CommunityにあるSource BSP File Formatのページを日本語訳したもの(Google 翻訳 + 分かりやすく)
面倒くさがった結果機械翻訳もびっくりな謎の訳が出来上がっているかもしれませんがご容赦ください……
また、読んでてよく分からなかった部分には(?)を置いてます。


Source BSP File Format

この記事は2005年10月のRofによる「The Source Engine BSP File Format」に基づいており、サービスが停止する前にGeocitiesから取得されています。元のバージョンへのミラーがここにあります。  

目次

  1. 前書き
  2. 概要
  3. BSPファイルのヘッダ
    1. バージョン
    2. Lump構造体
    3. Lumpの種類
    4. Lumpの圧縮
  4. Lump
    1. Plane
    2. Vertex
    3. Edge
    4. Surfedge
    5. FaceとOriginal Face
    6. BrushとBrushside
    7. NodeとLeaf
    8. LeaffaceとLeafbrush
    9. Texture
      1. Texinfo
      2. Texdata
      3. TexdataStringDataとTexdataStringDataTable
    10. Model
    11. Visiblity
    12. Entity
    13. Gamelump
      1. Static Prop
      2. その他
    14. Displacement
      1. DispInfo
      2. DispVerts
      3. DispTris
    15. Pakfile
    16. Cubemap
    17. Overlay
    18. Lighting
    19. Ambient Lighting
    20. Occlusion
    21. Physics
    22. その他

前書き

このドキュメントでは、Source Engineで使用されるBSPファイルの構造について説明します。このファイル形式は、Half-Life 1のエンジン(GoldSrc)のものと似ていますが、同じではありません。GoldSrcのBSPファイルはQuake、Quake II、QuakeWorldおよびQuake III Arenaのフォーマットに基づいています。このため、Max McGuireの記事 Quake 2 BSP File Formatは全体的な構造と、類似している部分の構造を理解する上で非常に役に立ちます。  

このドキュメントは、RofがVMEX(Half-Life 2 BSPファイルの逆コンパイラ)の作成中に彼が書いたノートを拡張したものです。したがって、マップの逆コンパイル(BSPファイルをHammer Map Editorで読み込み可能なVMFファイルに変換すること)を実行するために必要な部分に焦点を当てています。

ここにあるほとんどの情報は、上記のMax McGuireの記事とSource SDKのソースコード(特に、C言語のヘッダーファイルであるpublic/bspfile.h)、そしてRof自身がVMEXの作成中に行った実験から得られたものです。  

このドキュメントは読者がC/C++、ジオメトリ、およびSourceのマッピング用語についてある程度の理解があることを想定しています。コード(主にC言語の構造体)は等幅フォントで書かれています。また、ここにある構造体は明瞭性と一貫性のため、SDKヘッダファイルにある実際の定義から変更されていることがあります。

概要

BSPファイルには、マップをレンダリングして遊ぶためにSource Engineで必要とされる大多数の情報が含まれています。これには以下のような情報が含まれます。

  • レベル内のすべてのポリゴンのジオメトリ
  • 各ポリゴンが描画される時に用いるテクスチャの名前と方向
  • ゲーム中にプレイヤーや他のアイテムの物理的挙動をシミュレートするために使われるデータ
  • マップ内のすべてのブラシベース、モデル(プロップ)ベース、不可視(論理)エンティティの位置とプロパティ  
  • BSP木と可視性テーブル(マップジオメトリ内のプレイヤーの位置の特定と、マップの見える範囲を可能な限り効率的にレンダリングするのに使用)  

また、マップファイルにはレベルで使用されているカスタムテクスチャやモデルをマップのPakfile lumpの中に任意で埋め込むこともできます(下記参照)。

BSPファイルに格納されていない情報として、マルチプレイヤーゲーム(Counter-Strike: SourceHalf-Life 2: Deathmatchなど)でマップを読み込んだ後に表示されるマップの説明テキスト(mapname.txtに格納されています)と、ノンプレイヤーキャラクター(NPC、マップをナビゲートする必要がある)が使用するAIナビゲーションファイル(mapname.navに格納されています)があります。Souce Engineのファイルシステムの仕組み上、これらの外部ファイルはBSPファイルのPakfile lumpに埋め込まれることもありますが、通常はそうではありません。  

公式マップのファイルは、Steam Game Cache File(GCF)形式で保存され、ゲームエンジンによりSteamファイルシステムを通じてアクセスされます。GCFファイルから中身を抽出してSteam外から閲覧するには、NemesisのGCFScapeを使用します。VPKファイル形式を使用している新しいゲームでは通常、マップはオペレーティングシステムのファイルシステムに直接保存されます。

BSPファイルのデータは、PC/Macではリトルエンディアンで、PS3/Xbox360ではビッグエンディアンで保存されます。リトルエンディアンのファイルをJavaなどのビッグエンディアン形式のプラットフォームで読み込む場合は、バイトスワップが必要です(逆も同様です)。  

BSPファイルのヘッダ

BSPファイルはヘッダから始まります。この構造は、ファイルがValve Source Engine BSPファイルであることと、フォーマットのバージョンを示して、その後にファイル内の各データ(lumpと呼ばれる)の場所、長さ、およびバージョンが最大64個分続きます。最後に、マップの修正回数が書かれています。  

ヘッダの構造はSDKの public/bspfile.h というヘッダファイルに記載されています。このファイルは、このドキュメント全体を通して広範囲にわたって参照されています。また、ヘッダは合計で1036バイトです。  

Alien Swarm Icon Alien Swarmでは、この構造体はBSPHeader_tに名前が変更されています。

struct dheader_t
{
	int	ident;                // BSPファイルの識別子
	int	version;              // BSPファイルのバージョン
	lump_t	lumps[HEADER_LUMPS];  // lumpディレクトリの配列
	int	mapRevision;          // マップの修正回数
};

identは、次のように定義された4バイトのマジックナンバーです。

// リトルエンディアン "VBSP"   0x50534256
#define IDBSPHEADER	(('P'<<24)+('S'<<16)+('B'<<8)+'V')

したがって、ファイルの最初の4バイトは常にVBSP(ASCII形式)です。これらのバイトはファイルをValve BSPファイルとして識別します。他のBSPファイルの形式では、異なるマジックナンバーを使用します(例えば、id SoftwareのQuake Engineを用いたゲームはIBSPで始まります)。GoldSrcのBSP形式では、マジックナンバーはまったく使用されません。また、マジックナンバーの順序はファイルのエンディアンを決定するためにも使用できます。VBSPはリトルエンディアンに、PSBVはビッグエンディアンに使用されます。

2番目のint値は、BSPファイル形式のバージョン(BSPVERSION)です。 Source ゲームの場合、この値はVampire: The Masquerade – Bloodlinesを除いて19から21の範囲です(下記の表を参照)。他のエンジン(Half-Life 1、Quakeシリーズなど)のBSPファイル形式は、全く異なるバージョン番号の範囲を使用することに注意してください。

バージョン

この表は、Source Engineを用いたいくつかのゲームで使用されているBSPバージョンの概要を示しています。

バージョン ゲーム 備考
17 Vampire: The Masquerade – Bloodlines dface_tに変更あり
17~18 Half-Life 2 Icon Half-Life 2 (Beta) ベータ版のリーク
19 Sin Episodes
19~20 Half-Life 2 Icon Half-Life 2 リリース時は19で、Source 2007/2009アップデートからは
部分的に20
Half-Life 2 Icon Half-Life 2: Deathmatch
Counter-Strike: Source Icon Counter-Strike: Source
Day of Defeat: Source Icon Day of Defeat: Source
20 Half-Life 2 Icon Half-Life 2: Episode One
Half-Life 2 Icon Half-Life 2: Episode Two
Half-Life 2 Icon Half-Life 2: Lost Coast
Garry's Mod Icon Garry's Mod
Team Fortress 2 Icon Team Fortress 2 最近のマップはStaticPropLump_tに変更あり(version = 10)
ゲームlump、エンティティ情報、PAKファイルはLZMA圧縮
Portal Icon Portal
Left 4 Dead dworldlight_tとStaticPropLump_tに変更あり(version = 8)
Zeno Clash StaticPropLump_tに変更あり(version = 7)
Dark Messiah dshader_t、StaticPropLump_t、texinfo_t、dgamelump_tに
変更あり
Vindictus 多くの変更された構造体を使用
The Ship StaticPropLump_tに変更あり
Bloody Good Time StaticPropLump_tに変更あり
Black Mesa: Source StaticPropLump_tに変更あり(version = 11)
21 Left 4 Dead 2 lump_tと古いdbrushside_t(?)に変更あり
Alien Swarm
Counter-Strike: Global Offensive StaticPropLump_tに変更あり(version = 10)
Dear Esther StaticPropLump_tに変更あり
Insurgency
The Stanley Parable
Tactical Intervention 256ビット XOR暗号化
22 Dota 2 早期ベータ版 dbrushside_tとddispinfo_tに変更あり
23 dbrushside_t、ddispinfo_t、doverlay_tに変更あり
27 Contagion
29 Titanfall 大幅に変更されている

ゲーム固有のBSP形式の詳細については、Source BSP File Format/Game-Specificを参照してください。

Lump構造体

次に、16バイトのlump_t構造体の配列が続きます。HEADER_LUMPSは64と定義されているため、全部で64個の要素があります。ただし、ゲームやバージョンによっては定義されていないものや空のものもあります。  

lump_tbspfile.hで定義されています。

struct lump_t
{
	int	fileofs;	// ファイルへのオフセット(バイト単位)
	int	filelen;	// Lumpの長さ(バイト単位)
	int	version;	// Lumpの形式のバージョン
	char	fourCC[4];	// Lump識別子
};

最初の2つのint値はbspファイルの先頭からのバイトオフセットとそのLumpに含まれるデータブロックのバイト長を表します。続いて、そのLumpの形式のバージョン番号(通常は0)と、通常0, 0, 0, 0である4バイトの識別子があります。圧縮されたLumpの場合、fourCCには非圧縮のLumpデータサイズが整数で示されています(詳細はLumpの圧縮の節を参照)。lump_t配列の未使用のメンバについてはすべての要素が0に設定されています。   Lumpのオフセット(と、それに対応するデータ)は最も近い4バイトの境界に切り上げられています。ただし、Lumpの長さについてはこの限りではありません。

Lumpの種類

lump_t配列が指すデータの種類は、配列内の位置によって定義されます。例えば、配列の最初のLump (Lump 0) は常にBSPファイルのエンティティデータです(下表参照)。BSPファイル内の実際のデータの位置は、そのLumpのoffsetとlengthによって定義されるため、ファイル内で特定の順番に並んでいる必要はありません。例えば、エンティティデータはLump配列の最初にあるにも関わらず通常はBSPファイルの最後に格納されます。したがって、lump_tヘッダの配列はLumpデータに関するディレクトリのようなものであり、Lumpデータはファイル内を自由に配置することができます。  

配列内におけるLumpの順番は以下のように定義されます。

番号 エンジン 名前 目的
0 Half-Life 2 Icon Source 2004 LUMP_ENTITIES マップ上のエンティティ
1 Half-Life 2 Icon Source 2004 LUMP_PLANES 平面の配列
2 Half-Life 2 Icon Source 2004 LUMP_TEXDATA テクスチャ名へのインデックス
3 Half-Life 2 Icon Source 2004 LUMP_VERTEXES 頂点の配列
4 Half-Life 2 Icon Source 2004 LUMP_VISIBLITY 圧縮された可視性に関するビット配列
5 Half-Life 2 Icon Source 2004 LUMP_NODES BSP木のノード
6 Half-Life 2 Icon Source 2004 LUMP_TEXINFO 面のテクスチャ配列
7 Half-Life 2 Icon Source 2004 LUMP_FACES 面の配列
8 Half-Life 2 Icon Source 2004 LUMP_LIGHTING ライトマップのサンプル
9 Half-Life 2 Icon Source 2004 LUMP_OCCLUSION オクルージョンポリゴンと頂点
10 Half-Life 2 Icon Source 2004 LUMP_LEAFS BSP木の葉ノード(リーフ)
11 Team Fortress 2 Icon Source 2007 LUMP_FACEIDS dfaceとHammerの面IDの関連付けと、
detail propを配置する時の乱数の種に使用
12 Half-Life 2 Icon Source 2004 LUMP_EDGES 辺の配列
13 Half-Life 2 Icon Source 2004 LUMP_SURFEDGES 辺へのインデックス
14 Half-Life 2 Icon Source 2004 LUMP_MODELS ブラシモデル
15 Half-Life 2 Icon Source 2004 LUMP_WORLDLIGHTS エンティティLumpから変換された内部の
ワールドライト(?)
16 Half-Life 2 Icon Source 2004 LUMP_LEAFFACES 各leafの面へのインデックス
17 Half-Life 2 Icon Source 2004 LUMP_LEAFBRUSHES 各leafのブラシへのインデックス
18 Half-Life 2 Icon Source 2004 LUMP_BRUSHES ブラシの配列
19 Half-Life 2 Icon Source 2004 LUMP_BRUSHSIDES ブラシサイドの配列
20 Half-Life 2 Icon Source 2004 LUMP_AREAS エリアの配列
21 Half-Life 2 Icon Source 2004 LUMP_AREAPORTALS エリア間のポータル
22 Half-Life 2 Icon Source 2004 LUMP_PORTALS Confirm 確認: 隣接するポリゴン間の境界を定義
するポリゴン?
Team Fortress 2 Icon Source 2007 LUMP_UNUSED0 未使用
Left 4 Dead 2 Icon Source 2009 LUMP_PROPCOLLISION 静的プロップの凸包リスト
23 Half-Life 2 Icon Source 2004 LUMP_CLUSTERS プレイヤーが入れるリーフ
Team Fortress 2 Icon Source 2007 LUMP_UNUSED1 未使用
Left 4 Dead 2 Icon Source 2009 LUMP_PROPHULLS 静的プロップの凸包
24 Half-Life 2 Icon Source 2004 LUMP_PORTALVERTS ポータルポリゴンの頂点
Team Fortress 2 Icon Source 2007 LUMP_UNUSED2 未使用
Left 4 Dead 2 Icon Source 2009 LUMP_PROPHULLVERTS 静的プロップの凸包の頂点
25 Half-Life 2 Icon Source 2004 LUMP_CLUSTERPORTALS Confirm 確認: 隣接するクラスタ間の境界を定義
するポリゴン?
Team Fortress 2 Icon Source 2007 LUMP_UNUSED3 未使用
Left 4 Dead 2 Icon Source 2009 LUMP_PROPTRIS 静的プロップの凸包三角形
へのインデックスとカウント(?)
26 Half-Life 2 Icon Source 2004 LUMP_DISPINFO Displacementの面配列
27 Half-Life 2 Icon Source 2004 LUMP_ORIGINALFACES 分割前のブラシ面の配列
28 Team Fortress 2 Icon Source 2007 LUMP_PHYSDISP Displacementの物理衝突判定データ
29 Half-Life 2 Icon Source 2004 LUMP_PHYSCOLLIDE 物理衝突判定データ
30 Half-Life 2 Icon Source 2004 LUMP_VERTNORMALS 面の平面の法線(?)
31 Half-Life 2 Icon Source 2004 LUMP_VERTNORMALINDICES 面の平面の法線(?)へのインデックス
32 Half-Life 2 Icon Source 2004 LUMP_DISP_LIGHTMAP_ALPHAS Displacementのライトマップのアルファ(?)
(Source 2006からは未使用もしくは空)
33 Half-Life 2 Icon Source 2004 LUMP_DISP_VERTS Displacementの表面メッシュの頂点
34 Half-Life 2 Icon Source 2004 LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS Displacementのライトマップサンプル位置
35 Half-Life 2 Icon Source 2004 LUMP_GAME_LUMP ゲーム固有のデータLump
36 Half-Life 2 Icon Source 2004 LUMP_LEAFWATERDATA 水中のリーフのためのデータ
37 Half-Life 2 Icon Source 2004 LUMP_PRIMITIVES 水ポリゴンデータ
38 Half-Life 2 Icon Source 2004 LUMP_PRIMVERTS 水ポリゴン頂点
39 Half-Life 2 Icon Source 2004 LUMP_PRIMINDICES 水ポリゴン頂点へのインデックスの配列
40 Half-Life 2 Icon Source 2004 LUMP_PAKFILE 埋め込まれた非圧縮のZip形式のファイル
41 Half-Life 2 Icon Source 2004 LUMP_CLIPPORTALVERTS クリップされたポータルポリゴンの頂点
42 Half-Life 2 Icon Source 2004 LUMP_CUBEMAPS env_cubemapの位置の配列
43 Half-Life 2 Icon Source 2004 LUMP_TEXDATA_STRING_DATA テクスチャ名のデータ
44 Half-Life 2 Icon Source 2004 LUMP_TEXDATA_STRING_TABLE LUMP_TEXDATA_STRING_DATA
へのインデックス配列
45 Half-Life 2 Icon Source 2004 LUMP_OVERLAYS info_overlayのデータ配列
46 Half-Life 2 Icon Source 2004 LUMP_LEAFMINDISTTOWATER リーフから水までの距離
47 Half-Life 2 Icon Source 2004 LUMP_FACE_MACRO_TEXTURE_INFO 面のマクロテクスチャ情報
48 Half-Life 2 Icon Source 2004 LUMP_DISP_TRIS Displacementの表面の三角形
49 Half-Life 2 Icon Source 2004 LUMP_PHYSCOLLIDESURFACE 圧縮されたWin32固有のHavok地形面衝突
判定データ(?) 廃止されたため使われない
Left 4 Dead 2 Icon Source 2009 LUMP_PROP_BLOB 静的プロップの三角形と文字列のデータ(?)
50 Half-Life 2 Icon Source 2006 LUMP_WATEROVERLAYS Confirm 確認: 水面の上にinfo_overlayが存在する?
51 Half-Life 2 Icon Source 2006 LUMP_LIGHTMAPPAGES Xboxでのライティング関係の別の実装
Team Fortress 2 Icon Source 2007 LUMP_LEAF_AMBIENT_INDEX_HDR LUMP_LEAF_AMBIENT_LIGHTING_HDRへの
インデックス
52 Half-Life 2 Icon Source 2006 LUMP_LIGHTMAPPAGEINFOS Xboxでのライティング関係の別の
インデックス
Team Fortress 2 Icon Source 2007 LUMP_LEAF_AMBIENT_INDEX LUMP_LEAF_AMBIENT_LIGHTINGへの
インデックス
53 Half-Life 2 Icon Source 2006 LUMP_LIGHTING_HDR HDRライトマップサンプル
54 Half-Life 2 Icon Source 2006 LUMP_WORLDLIGHTS_HDR エンティティLumpから変換された内部の
HDRワールドライト
55 Half-Life 2 Icon Source 2006 LUMP_LEAF_AMBIENT_LIGHTING_HDR リーフごとのアンビエントライト
サンプル(HDR)
56 Half-Life 2 Icon Source 2006 LUMP_LEAF_AMBIENT_LIGHTING リーフごとのアンビエントライト
サンプル(LDR)
57 Half-Life 2 Icon Source 2006 LUMP_XZIPPAKFILE Xboxで、XZipバージョンのpakファイル
廃止されている
58 Half-Life 2 Icon Source 2006 LUMP_FACES_HDR HDRマップは異なる面データを持つことが
ある
59 Half-Life 2 Icon Source 2006 LUMP_MAP_FLAGS レベル全体の拡張フラグ
すべてのレベルに存在するわけではない
60 Team Fortress 2 Icon Source 2007 LUMP_OVERLAY_FADES オーバーレイのフェード距離
61 Left 4 Dead Icon Source 2008 LUMP_OVERLAY_SYSTEM_LEVELS システムレベル設定(このオーバーレイを
描画するための最小/最大CPU&GPU)
62 Left 4 Dead 2 Icon Source 2009 LUMP_PHYSLEVEL To do
63 Alien Swarm Icon Source 2010 LUMP_MULTIBLEND Displacementのマルチブレンド情報

Lump 53~56はバージョン20以上のBSPファイルでのみ使用されます。Lump 22~25はバージョン20では使用されていません。 既知の要素について、データLumpの構造を以下に説明します。Lumpの多くは単純な構造の配列で、またいくつかはその内容に応じて可変長です。各データの最大サイズまたは要素数はbspfile.hでもMAX_MAP_*として定義されています。

最後に、ヘッダはマップリビジョン番号を表すint値で終わります。この数値は、マップのvmfファイルのリビジョン番号(mapversion)に基いています。これは、Hammer Editorで保存される度に増加する値です。

ヘッダのすぐ後ろには、最初のデータLumpが続いています。実際には最初のデータLumpはLump 1(平面データ配列)ですが、これは前のリスト中ののoffsetフィールドで指定された任意のLumpです。

Lumpの圧縮

PlayStation 3やXbox 360などのコンソールプラットフォーム用のBSPファイルは通常、LZMAで圧縮されたLumpを格納しています。この場合、Lumpデータは次のヘッダで始まります(public/tier1/lzmaDecoder.hより)。

struct lzma_header_t
{
	unsigned int	id;
	unsigned int	actualSize;		// 常にリトルエンディアン
	unsigned int	lzmaSize;		// 常にリトルエンディアン
	unsigned char	properties[5];
};

idは以下のように定義されています。

// リトルエンディアン "LZMA"
#define LZMA_ID	(('A'<<24)|('M'<<16)|('Z'<<8)|('L'))

圧縮に関して、2つのスペシャルケースがあります。LUMP_PAKFILEは決して圧縮されず、LUMP_GAME_LUMPの各Gamelumpは個別に圧縮されます。圧縮されたGamelumpのサイズは、現在のGamelumpのオフセットを次のもののオフセットから引くことで決めることができます。このため、最後のGamelumpは常にオフセットを含む空のダミーです。

Lump

平面

BSPジオメトリの基礎は、BSP木構造全体の分割面として使われる平面によって定義されます。

平面Lump (Lump 1)dplane_t構造体の配列です。

struct dplane_t
{
	Vector	normal;	// 法線ベクトル
	float	dist;	// 原点からの距離
	int	type;	// 平面軸識別子(?)
};

Vector型は以下のように定義される3次元ベクトルです。

struct Vector
{
	float x;
	float y;
	float z;
};

float型は4バイトの長さを持つため、1つの平面につきサイズは20バイトで、平面Lumpのサイズは20の倍数です。

平面は、その面に垂直な単位ベクトル(長さが1.0のベクトル)である法線ベクトルを示す要素normalによって表現されます。平面の位置は、マップの原点(0, 0, 0)から平面上の最も近い点までの距離distによって与えられます。

数学的には、平面は方程式
Ax + By + Cz = D
を満たす点(x, y, z)の集合として記述されます。ここで、点(A, B, C)は平面の法線ベクトル、すなわちnormalで、Dはdistです。各平面は無限に広がっていて、マップ全体を平面の上(F = 0)、平面の前(F > 0)、平面の後ろ(F < 0)の3つに分割します。

平面は特定の向きを持ち、それによって平面の前と後ろが対応づけられます。また、平面の向きを反転するにはA、B、C、Dの各要素を反転します。

この構造体のtypeメンバは座標軸に垂直な平面を示すフラグが含まれているようですが、通常は使用されません。

マップには最大で65536枚の平面が存在します(MAX_MAP_PLANES)。

頂点

頂点Lump (Lump 3) はマップジオメトリのブラシのすべての頂点(コーナー)座標の配列です。各頂点は3つの浮動小数点数からなるVector型で表され、1つにつき12バイトのサイズを持ちます。

2つの面で頂点が正確に一致する場合、頂点はそれらの面で共有されることに注意してください。

マップには最大で65536個の頂点が存在します(MAX_MAP_VERTS)。

辺Lump (Lump 12) はdedge_t構造体の配列です。

struct dedge_t
{
	unsigned short	v[2];	// 頂点インデックス
};

各辺は単なる頂点インデックス(頂点Lumpの配列へのインデックス)の組です。辺は2頂点間の直線として定義されます。通常、辺LumpはSurfedge配列を介して参照されます(下記)。

頂点と同じように、隣接する面の間で辺を共有することができます。

マップには最大で256000本の辺が存在します(MAX_MAP_EDGES)。

Surfedge

「Surface edge lump」の略であると推定されるSurfedge Lump (Lump 13) は(符号付きの)整数の配列です。Surfedgeはやや複雑な方法で辺の配列を参照するために使用されます。Surfedge配列は正または負の値を取ります。この数値の絶対値は辺の配列へのインデックスです。もしこの数値が正の数である場合は、辺が最初の頂点から2番めの頂点の方向に定義されていることを意味しています。負の数の場合は、2番めの頂点から最初の頂点へ向かうように定義されています。

この方法により、Surfedge配列は辺を特定の方向に向かうように参照できます(辺を方向づけする理由については、以下の面の項目を参照してください)。

1つのマップに付き512000個のSurfedgeまでという制限があります(MAX_MAP_SURFEDGES)。ただし、Surfedgeの数は必ずしもマップの辺の数と同じではありません。

面と元の面

面Lump (Lump 7) にはプレイヤーの視点をレンダリングするためにゲームエンジンによって使用されるマップの主要なジオメトリを含まれます。面LumpにはBSP分割処理を行った後の面が含まれています。したがって、面Lumpに含まれる面はHammer Editorで作成されたブラシの面には直接対応しません。また、面は常に平らな凸多角形ですが、同一直線上にある頂点を含むことができます。

面Lumpはマップファイルの中でも複雑な構造の1つです。このLumpは、56バイトの長さを持つdface_tの配列です。

struct dface_t
{
	unsigned short	planenum;			// 平面番号
	byte		side;				// 面の向きと平面番号による平面の向きが反対なら1
	byte		onNode;				// ノードにあるなら1、リーフにあるなら0
	int		firstedge;			// Surfedge Lumpへのインデックス
	short		numedges;			// 面を構成するSurfedgeの数
	short		texinfo;			// テクスチャ情報
	short		dispinfo;			// Displacement情報
	short		surfaceFogVolumeID;		// ?
	byte		styles[4];			// 切り替え可能なライティングの情報
	int		lightofs;			// ライトマップLumpへのオフセット
	float		area;				// 面の面積(単位はHammer units^2)
	int		LightmapTextureMinsInLuxels[2];	// テクスチャライティング情報
	int		LightmapTextureSizeInLuxels[2];	// テクスチャライティング情報
	int		origFace;			// この面の分割元となった元の面
	unsigned short	numPrims;			// プリミティブ
	unsigned short	firstPrimID;
	unsigned int	smoothingGroups;		// ライトマップスムージンググループ
};

最初のメンバであるplanenumは平面番号(この面と位置の合う平面Lumpへのインデックス)です。この時、参照された平面がこの面と同じ方向を向いている場合、sideは0です。そうでない場合は、非ゼロです。

facexray

(3つのタイプのジオメトリデータを用いたd1_trainstation_01のX線ビューをアニメーションしたもの。フルサイズで見るにはこちら。)

firstedgeはSurfedge配列へのインデックスです。firstedge番目のSurfedgeからそれに続くnumedges個のSurfedgeが面を構成する辺を定義します。上で述べたように、Surfedge配列の値が正か負かは、辺の配列に格納された対応する頂点の組をどちらの方向にたどるかを示します。したがって、面を構成する頂点は面に向かって見ると時計回りの順になるように参照されます。これによって面のレンダリングが容易になり、視点から離れた面を高速に間引きすることができます。

texinfoはTexInfo配列へのインデックスで(以下参照)、面に描かれるべきテクスチャを示します。dispinfoはDispInfo配列へのインデックスで、0以上の場合は面はDisplacementであり、Displacementの境界を定義します。そうでない場合は、dispinfoは-1です。surfaceFogVolumeIDはプレイヤーの視点が水中にあるか水面を見ている時にフォグを描画することに関係しているように思われます。

origFaceはこの面の分割される元となった「元の面(Original Face)」へのインデックスです。numPrimsfirstPrimIDは「非ポリゴンプリミティブ」(以下参照)の描画に関連しています。dface_t構造体の他のメンバは、面のライティング情報を参照するために使用されます(下記のライティングLumpを参照)。

面の数は65536枚に制限されています(MAX_MAP_FACES)。

元の面Lump (Lump 27) は面Lumpと同じ構造を持ちますが、BSP分割処理が行われる前の面の配列が含まれています。したがって、これらの面は面の配列よりもマップのコンパイル前に存在する元のブラシ面に近く、また面の数はより少ないです。元の面のorigFaceはすべて0です。また、元の面の配列の要素数も最大で65536枚です。

面と元の面の両方がカリングされます。つまり、マップのコンパイル前に存在する多くの面(主にマップの境界より外側の方を向いている面)が配列から削除されます。

ブラシとブラシ側面

ブラシLump (Lump 18) にはコンパイル前のVMFファイルに含まれていたすべてのブラシが含まれています。面とは異なり、ブラシは辺や頂点の代わりに平面を使う空間領域構成法(CSG)で定義されています。Source BSPファイルにはブラシとブラシ側面のLumpが存在するため、この情報が存在しないGoldSrcのファイルよりもかなり容易に逆コンパイルできます。このLumpは12バイトの長さを持つdbrush_t構造体の配列です。

struct dbrush_t
{
	int	firstside;	// 最初のブラシ側面(へのインデックス)
	int	numsides;	// ブラシ側面の数
	int	contents;	// コンテンツフラグ
};

最初のint値firstsideはブラシ側面Lumpへのインデックスで、firstside番目のブラシ側面からnumsides枚のブラシ側面がこのブラシのすべての側面を構成します。contentsにはこのブラシの内容を決定するビットフラグが格納されています。値はビットOR演算されたもので、フラグはpublic/bspflags.hで定義されています。

名前 備考
CONTENTS_EMPTY0空の空間
CONTENTS_SOLID0x1固体の中では目が見えない
CONTENTS_WINDOW0x2半透明だが、水ではない(つまりガラス)
CONTENTS_AUX0x4
CONTENTS_GRATE0x8アルファテストされた「鉄格子」のテクスチャで、弾丸と視線は通過するが、固体は通過しない
CONTENTS_SLIME0x10
CONTENTS_WATER0x20
CONTENTS_MIST0x40
CONTENTS_OPAQUE0x80AIの視線を遮断する
CONTENTS_TESTFOGVOLUME0x100透けて見えないもの(固体ではないかもしれないにもかかわらず)
CONTENTS_UNUSED0x200未使用
CONTENTS_UNUSED60x400未使用
CONTENTS_TEAM10x800チームごとにプレイヤーやオブジェクトの衝突判定を区別するために使用される
CONTENTS_TEAM20x1000
CONTENTS_IGNORE_NODRAW_OPAQUE0x2000SURF_NODRAWを持つ面ではCONTENTS_OPAQUEを無視する
CONTENTS_MOVEABLE0x4000MOVETYPE_PUSHのエンティティ(ドア、足場など)にあたる
CONTENTS_AREAPORTAL0x8000以下のフラグは不可視で、ブラシを侵食しない
CONTENTS_PLAYERCLIP0x10000
CONTENTS_MONSTERCLIP0x20000
CONTENTS_CURRENT_00x40000CURRENT系は他のフラグに追加されるもので、複数追加されることもある
CONTENTS_CURRENT_900x80000
CONTENTS_CURRENT_1800x100000
CONTENTS_CURRENT_2700x200000
CONTENTS_CURRENT_UP0x400000
CONTENTS_CURRENT_DOWN0x800000
CONTENTS_ORIGIN0x1000000エンティティをBSP処理する前に削除される
CONTENTS_MONSTER0x2000000ブラシに与えられるフラグではなく、ゲーム中に関わる
CONTENTS_DEBRIS0x4000000
CONTENTS_DETAIL0x8000000VisLeaf処理の後にブラシに追加される
CONTENTS_TRANSLUCENT0x10000000面に透明度がある場合に自動的に付与
CONTENTS_LADDER0x20000000
CONTENTS_HITBOX0x40000000トレースで正確なヒットボックスを使うためのもの(?)

これらのフラグの一部は、以前のゲームエンジンから引き継がれているように見え、Sourceのマップでは使われていないものもあります。また、これらのフラグはマップのリーフの中身について説明するためにも使われます(下記参照)。CONTENTS_DETAILフラグはマップのコンパイル前にfunc_detailエンティティであったブラシをマークするために使われます。

ブラシの配列の要素数は8192個に制限されています(MAX_MAP_BRUSHES)。

ブラシ側面Lump (Lump 19) は8バイトの構造体の配列です。

struct dbrushside_t
{
	unsigned short	planenum;	// リーフの外側を向いた平面
	short		texinfo;	// テクスチャ情報
	short		dispinfo;	// Displacementの情報
	short		bevel;		// 側面が斜めなら1
};

planenumは平面の配列へのインデックスで、そのブラシ側面に対応する平面を示します。texinfodispinfoはテクスチャとDisplacement情報のLumpへの参照を示します。bevelは普通のブラシ側面なら0ですが、斜面の場合は1です(衝突判定に使われるものと思われます)。

面の配列とは異なり、ワールドの外を向いたブラシ側面はカリング(削除)されません。その代わり、コンパイル処理中にテクスチャ情報がtools/[toolsnodraw]に変更されます。ここで注意すべきことは、ブラシをレンダリングするのに使われる対応する面の配列の要素と、ブラシとブラシ側面とを関連付ける直接的な方法が存在しないことです。ブラシ側面はすべてのプレイヤーとワールドブラシとの衝突判定を計算するためにエンジンによって使われます(VPhysicsオブジェクトは代わりにLump 29が使われます)。

ブラシ側面は最大で65536枚です(MAX_MAP_BRUSHSIDES)。また、ブラシごとのブラシ側面の最大数は128枚です(MAX_BRUSH_SIDES)。

ノードとリーフ

ノード配列 (Lump 5) とリーフ配列 (Lump 10) はマップのBSP木(Binary Space Partition Tree)を定義します。BSP木はマップのジオメトリに対するプレイヤーの視点の位置と、可視性情報(下記参照)からマップのどの部分が描画されるかを素早く決定するためにエンジンによって使用されます。

ノードとリーフは木構造を構築します。各リーフはマップのボリュームを定義したものを表し、各ノードはすべての子ノードの総ボリュームを表します。

各ノードには必ず2つの子ノードがあり、子ノードは別のノードかリーフです。子ノードさらに2つの別の子ノードを持っていて、これは木のすべての分岐がリーフを指すまで続きます。また、各ノードは平面の配列内にある平面も参照します。プレイヤーの視点を決定する時、エンジンは視点がどのリーフの中にあるかを探します。この時、根ノード(ノード0)が参照している平面と視点の座標を比較し、平面の前に視点がある場合は最初の子ノードへ、後ろにある場合は2番めの子ノードへ移動します。比較するノードがリーフになるまでこれを続けることで、視点がどのリーフの中に存在するかを判断することができます。したがって、エンジンは視点の位置が見つかるまでBSP木を走査します。そして、リーフは親ノードの平面によって定義される重ならない凸なボリュームとしてマップのボリュームを分割します。

BSP木がどのように構築されるかについての詳細は、「BSP for dummies」の記事を参照してください。

ノード配列は32バイトの構造体で構成されています。

struct dnode_t
{
	int		planenum;	// 平面の配列へのインデックス
	int		children[2];	// 負の値は -(leafs + 1)番目のリーフを表す
	short		mins[3];	// 視錐台カリング用
	short		maxs[3];
	unsigned short	firstface;	// 面の配列へのインデックス
	unsigned short	numfaces;	// 両面をカウントする
	short		area;		// すべての子ノードが同じエリアの場合はエリアへのインデックス
					// そうでない場合は-1
	short		paddding;	// 32バイトの長さを持つpad(?)
};

planenumは平面の配列の要素を表します。children[]メンバはこのノードが持つ2つの子ノードです。もしこの値が正なら、ノードへのインデックスで、負なら、 -1-childはリーフの配列へのインデックスを表します(例えば、-100は99番目のリーフを参照します)。

mins[]maxs[]メンバはノードを囲むバウンディングボックスの座標です。firstfacenumfacesはこのノードに含まれているマップの面を表す面の配列へのインデックスです。0の場合は面が含まれていません。areaの値はこのノードにおけるマップのエリアです(下記参照)。マップには最大65536個のノードが存在します(MAX_MAP_NODES)。

リーフ配列は要素が56バイトの長さを持つ配列です。

struct dleaf_t
{
	int			contents;		// ブラシをすべてビットORしたもの(不必要?)
	short			cluster;		// リーフ内のクラスタの数
	short			area:9;			// リーフ内のエリアの数
	short			flags:7;		// フラグ
	short			mins[3];		// 視錐台カリング用
	short			maxs[3];
	unsigned short		firstleafface;		// リーフ面へのインデックス
	unsigned short		numleaffaces;
	unsigned short		firstleafbrush;		// リーフブラシへのインデックス
	unsigned short		numleafbrushes;
	short			leafWaterDataID;	// 水中でない場合は-1
 
	//!!! バージョン19以前のマップは以下のコメントブロックを外す
	/*
	CompressedLightCube	ambientLighting;	// エンティティ用 計算済みのライティング情報
	short			padding;		// 4バイトの境界のためのpadding(?)
	*/
};

リーフの構造は子と平面への参照を持たないことを除いてノードのそれと似ています。追加の要素としてcontentsフラグ(前述のブラシLumpを参照)とcluster(クラスタの数、下記参照)があります。contentsフラグはリーフ内のブラシの中身を示すフラグです。areaflagsは16ビットの空間を共有していて、下位9ビットがarea、上位7ビットがflagsです。areaはエリアナンバーを、flagsはリーフに関するフラグを示します。firstleaffacenumleaffacesはリーフ面の配列へのインデックスで、リーフ内に面がある時にどの面があるかを表します。firstleafbrushnumleafbrushesも同様にリーフブラシの配列を通してリーフ内にあるブラシへのインデックスを表します。

ambientLighitngという要素は24バイトのCompressedLightCube構造体で、リーフ内にあるオブジェクトのライティングに関するものです。バージョン17のBSPファイルはdleaf_t構造体がアンビエントライティングのデータを省くように変更されていて、リーフごとのサイズが32バイトになっています。同じ構造体はバージョン20のBSPファイルでも用いられていて、LDRとHDRのためのアンビエントライティングの情報はおそらく新しいLumpであるLump 55とLump56に格納されています。

すべてのリーフは凸多面体で、親ノードの平面により定義されます。リーフは重ならず、マップ内の任意の点はただ1つのリーフの中に属します。固体のブラシで満たされていないリーフはプレイヤーが入ることが可能で、そのようなリーフはクラスタ番号が設定されています。これは可視性の情報と一緒に使用されます(下記)。

マップには通常複数の独立したBSP木が存在します。各木はモデル配列(下記)の要素に対応し、モデル配列の要素には各木の根ノードへの参照があります。最初のBSP木はworldspawnでモデルで、レベル全体のジオメトリです。続くBSP木はマップの各ブラシエンティティのモデルです。

BSP木の作成はマップのコンパイルの第一段階でVBSPプログラムにより行われます。マップの制作者によるHINTブラシやfunc_detailの使用と、すべてのブラシの注意深い配置は、どのようにBSP木が作られマップがリーフに分割されるかに影響を及ぼすことがあります。

リーフ面とリーフブラシ

リーフ面Lump (Lump 16) はunsigned short型の配列で、各要素は面の配列へのインデックスです。リーフブラシLump(これもunsigned short型の配列です) (Lump 17) も同様のことをブラシに対して行います。これらの最大サイズはどちらも65536個です(MAX_MAP_LEAFFACESMAX_MAP_LEAFBRUSHES)。

テクスチャ

マップのテクスチャ情報は様々なLumpに分割されています。Texinfo Lumpが最も基本的なもので、面やブラシ側面の配列から参照され、他のテクスチャ関係のLumpを参照します。

Texinfo

Texinfo Lump (Lump 6)texinfo_t構造体の配列です。

struct texinfo_t
{
	float	textureVecs[2][4];	// [s/t][xyz オフセット]
	float	lightmapVecs[2][4];	// [s/t][xyz オフセット] - 長さの単位はtexels/area
	int	flags;			// miptex フラグ オーバーライド
	int	texdata;		// テクスチャ名やサイズなどへのポインタ
}

各Texinfoのサイズは72バイトです。

最初のfloat型配列は、ワールドジオメトリ上にレンダリングされる時のテクスチャの方向とスケーリングを表す2つのベクトルです。 2つのベクトルstは、テクスチャピクセル座標空間における左から右へ、下から上への方向をワールドにマッピングするものです。各ベクトルには、x、y、z成分と、ワールドに対するテクスチャの「シフト」のオフセットがあります。 ベクトルの長さは、各方向へのテクスチャのスケーリングを表します。

テクスチャピクセル(またはテクセル)の2次元座標(u, v)は、次式によって面上の点のワールド座標(x、y、z)にマッピングされる。

u = tv0,0 * x + tv0,1 * y + tv0,2 * z + tv0,3

v = tv1,0 * x + tv1,1 * y + tv1,2 * z + tv1,3

(すなわち、その方向へのオフセットと頂点のベクトルとの内積です。ここで、tvA, BtextureVecs[A][B]です。)

さらに、(u, v)を計算してからグラフィックスカードに送るテクスチャ座標に変換するには、uとvをテクスチャの幅と高さでそれぞれ割ります。

lightmapVecsは、テクスチャのライトマップサンプルをワールドに同様にマッピングします。

flagsにはbspflags.hで定義されているビットフラグが含まれます。

名前 備考
SURF_LIGHT 0x1   値は光の強さを保持する
SURF_SKY2D 0x2   描画しない、2Dスカイボックスを描画することを示し、3Dスカイボックスは描画しない
SURF_SKY 0x4   描画しないが、スカイボックスに追加する
SURF_WARP 0x8   乱流ワープ(?)
SURF_TRANS 0x10   テクスチャは半透明
SURF_NOPORTAL 0x20   この表面にはポータルを置くことができない
SURF_TRIGGER 0x40   Xboxでオクルーダーに不具合が生じるため回避策としてトリガーサーフェスを消すためのもの
SURF_NODRAW 0x80   テクスチャの参照をしないようにするもの
SURF_HINT 0x100 BSPの境界面を作る
SURF_SKIP 0x200 完全に無視し、閉じてないブラシを可能にする
SURF_NOLIGHT 0x400 ライティングを計算しない
SURF_BUMPLIGHT 0x800 バンプマップ付きの表面のために3つのライトマップを計算する
SURF_NOSHADOWS 0x1000 影を落とさない
SURF_NODECALS 0x2000 デカールを受け取らない
SURF_NOCHOP 0x4000 この表面は分割されない
SURF_HITBOX 0x8000 この面はヒットボックスの一部である

フラグは、テクスチャの.vmtファイルの内容から派生しているように見え、そのテクスチャの特殊なプロパティを指定します。

Texdata

最後に、texdataはTexdata配列へのインデックスで、実際のテクスチャを指定します。

Texinfoのインデックス(面やブラシ側面から参照される)は-1が与えられることがあります。これはテクスチャ情報がその面に関連付けられていないことを示し、テクスチャのタイプにSKIP、CLIP、もしくはINVISIBLEを指定したブラシ面をコンパイルすると発生します。

Texdata配列 (Lump 2) は以下の構造体で構成されています。

struct dtexdata_t
{
	Vector	reflectivity;		// RGB反射率
	int	nameStringTableID;	// TexdataStringTableへのインデックス
	int	width, height;		// 元画像
	int	view_width, view_height;
};

reflectivityベクトルは、マテリアルの.vtfファイルから取り出されたテクスチャの反射率のRGB成分に対応します。これは、テクスチャ表面からどのように光が反射するかのラジオシティ(照明)の計算におそらく使用されます。nameStringTableIDはTexdataStringTableへのインデックスです(下記)。 他のメンバは、テクスチャのソースイメージに関連しています。

TexdataStringDataとTexdataStringTable

TexdataStringTable (Lump 44) はint型の配列で、TexdataStringData (Lump 43) へのインデックスです。TexdataStringData Lumpはヌル終端文字列で表されたテクスチャ名を連結したものです。

マップには最大12288個のTexinfoが存在します(MAX_MAP_TEXINFO)。Texdataの制限は最大2048個です(MAX_MAP_TEXDATA)。また、TexdataStringDataのサイズは最大256000バイトです(MAX_MAP_TEXDATA_STRING_DATA)。そして、テクスチャ名は最大128文字までです(TEXTURE_NAME_LENGTH)。

Model

ModelとはBSPファイル形式の用語で、しばしば「bmodel」とも呼ばれるブラシと面の集合のことです。Source SDKで「studiomodel」と呼ばれるHammer Editorで使用されるプロップモデルの方と混同しないように注意してください。

Model Lump (Lump 14) は24バイトのdmodel_t構造体で構成されています。

struct dmodel_t
{
	Vector	mins, maxs;		// バウンディングボックス
	Vector	origin;			// サウンドやライティング用
	int	headnode;		// ノード配列へのインデックス
	int	firstface, numfaces;	// 面の配列へのインデックス
};

minsmaxsはModelのバウンディングボックスを示す点です。originが設定されている場合、Modelの原点座標がその点であることを意味します。headnodeはこのModelを表すBSP木の根ノードを示すノード配列へのインデックスです。firstfacenumfacesは面の配列へのインデックスで、Modelを構成する面を示します。

この配列にある最初のModel(Model 0)は常に「worldspawn」(エンティティ以外のマップ全体のジオメトリとfunc_detailブラシの集合)です。続くModelはブラシエンティティに関連付けられるもので、エンティティLumpから参照されます。

マップには最大で1024個のModelが存在します(MAX_MAP_MODELS)。これにはworldspawnのModelも含みます。

可視性

可視性Lump (Lump 4) はこれまでに解説したものとはやや異なった形式のLumpです。これを理解するためには、Source Engineの可視性システムがどのように機能するかについての議論が必要です。

ノードとリーフの節で述べたように、マップ内の任意の点はリーフと呼ばれる凸ボリュームに分類されます。マップ内の(外側の空間に触れていない)ブラシで覆われていないリーフは、プレイヤーの視点を含む可能性があります。このようなプレイヤーの入れるリーフ(visleaf とも呼ばれる)にはクラスタ番号が割り当てられます。Source BSPファイルでは1つの進入可能なリーフには1つのクラスタ番号が対応しています。

(用語がここでは少し分かりにくくなっています。「Quake 2 BSP File Format」の記事によると、Q2 Engineでは各クラスタに複数の隣接するリーフが存在する可能性があります。したがって、クラスタはリーフの集まりとみなせるためクラスタという名前で呼ばれています。この状況は、Sourceのマップをコンパイルする時にも発生することがあります。しかしながら、VVISのコンパイル処理が終了した後、これらの隣接するリーフ(と、それらの親ノード)は通常1つのリーフに統合されます。古いSourceのマップ(Counter-Strike: Global Offensiveより前)では、クラスタごとにリーフは1つしかないようですが、いくつかのCS:GOのマップでは最終的にコンパイルされたBSP内で、複数のリーフが同じクラスタに属することがあります。これは、ほとんどのCS:GOマップの3Dスカイボックスで特に起こると思われ、de_cbbleやde_nukeなどの最近改装されたマップの主なプレイ可能領域から見ることができます。)

各クラスタは、プレイヤーが存在する可能性を持つマップ内のボリュームです。マップを素早くレンダリングするために、ゲームエンジンは現在のクラスタから見えるクラスタについてのみジオメトリを描画します。プレイヤーのいるクラスタから完全に見えないクラスタを描く必要はありません。クラスタ間の可視性の計算はVVISコンパイルツールの役目で、その結果得られるデータは可視性Lumpに格納されます。

エンジンがあるクラスタが見えると認識すると、リーフデータはそのクラスタに存在するすべての面を参照し、結果としてそのクラスタの内容をレンダリングすることができます。

データはビットベクトルの配列として格納されます。各クラスタについて、他のどのクラスタが見えるかのリストはn番目のビットがn番目のクラスタに対応するビット配列(1なら見える、0なら見えない)として配列に格納されます。これはクラスタのPotentially Visiblity Set(PVS)として知られています。このデータはサイズが大きいため、各ビットベクトルは0ビットのランレングス符号化グループによって圧縮されます。

また、各クラスタに対してPotentially Audible Set(PAS)の配列も作成されます。これはあるクラスタで発生する音をどのクラスタで聞くことができるかを表します。PASは、現在のクラスタのPVS内にあるすべてのクラスタのPVSビットを統合することで作成されるようです。

可視性Lumpは以下のように定義されます。

struct dvis_t
{
	int	numclusters;
	int	byteofs[numclusters][2]
};

最初のint値はマップ内の総クラスタ数です。その後にはint型配列が続き、この配列はLumpの先頭から各クラスタのPVSビット配列の開始点とPAS配列の開始点へのオフセットです。配列の後には圧縮されたビットベクトルがあります。

ランレングス圧縮の復号化は次のように動作します。特定のクラスタのPVSを見つけるためには、byteofs[]配列内のオフセットによって指定されたバイトから開始します。PVSバッファの現在のバイトが0の場合、次のバイトの値に8をかけた値がスキップするクラスタの数で、これはそのクラスタからは見えないクラスタです。現在のバイトが0でない場合は、設定されているビットがそのクラスタから見えるクラスタに対応します。これがマップ内の総クラスタ数の分だけ続きます。

ビットベクトルを展開するCコードの例は「Quake 2 BSP File Format」のドキュメントにあります。

可視性Lumpの最大サイズは0x1000000バイトです(MAX_MAP_VISIBLITY)。すなわち、16MBです。

エンティティ

関連: Patching levels with lump files

エンティティLump (Lump 0) はエンティティのデータをコンパイル前のVMFファイルにあるKeyValueフォーマットに非常によく似た形式で格納するASCIIテキストバッファです。一般的な形式は次の通りです。

{
	"world_maxs" "480 480 480"
	"world_mins" "-480 -480 -224"
	"maxpropscreenwidth" "-1"
	"skyname" "sky_wasteland02"
	"classname" "worldspawn"
}
{
	"origin" "-413.793 -384 -192"
	"angles" "0 0 0"
	"classname" "info_player_start"
}
{
	"model" "*1"
	"targetname" "secret_1"
	"origin" "424 -1536 1800"
	"Solidity" "1"
	"StartDisabled" "0"
	"InputFilter" "0"
	"disablereceiveshadows" "0"
	"disableshadows" "0"
	"rendermode" "0"
	"renderfx" "0"
	"rendercolor" "255 255 255"
	"renderamt" "255"
	"classname" "func_brush"
}

エンティティは中括弧([])に囲まれて定義され、引用符で囲まれたキーと値のペアを各行にリストします。最初のエンティティは常にworldspawnです。classnameプロパティはエンティティの種類を指定し、エンティティの名前がHammerで定義されていればtargetnameがその名前を指します。modelプロパティはアスタリスク(*)で始まる場合は少し特殊で、続く数字はブラシエンティティのModelに対応するModel配列(上記)へのインデックスです。それ以外の場合、modelプロパティはコンパイルされたモデルの名前を表します。他のキーと値のペアは、Hammerで設定されたエンティティのプロパティに対応します。

noteicon 注釈: エンティティのうちのいくつか(func_detail、env_cubemap、info_overlay、prop_staticを含む)は内部的なもので、通常はワールドに吸収されるためコンパイル処理中にエンティティLumpから削除されます。

Source Engineのバージョンに応じて、エンティティLumpには4096(Source 2004)から16384(Alien Swarm)個のエンティティを含むことができます(MAX_MAP_ENTITIES)。これらの制限は、エンジンの実際のエンティティの制限とは無関係です。各キーは最大32文字まで(MAX_KEY)、値は最大1024文字までです(MAX_VALUE)。

Gamelump

Gamelump (Lump 35) は、Source Engineを使用したゲーム固有のマップデータに使用されるように意図されているため、以前に定義されたフォーマットを変更することなくファイルフォーマットを拡張することができます。GamelumpはGamelumpヘッダから始まります。

struct dgamelumpheader_t
{
	int lumpCount;	// Gamelumpの数
	dgamelump_t gamelump[lumpCount];
};

Gamelumpディレクトリ配列は次のように定義されます。

struct dgamelump_t
{
	int		id;		// Gamelump ID
	unsigned short	flags;		// フラグ
	unsigned short	version;	// Gamelumpバージョン
	int		fileofs;	// このGamelumpへのオフセット
	int		filelen;	// 長さ
};

Gamelumpはどのデータが格納されているかを定義する4バイトのidメンバによって識別され、データのバイト位置と長さはfileofsfilelenで与えられます。fileofsはBSPファイル先頭からの相対的なオフセットであり、Gamelumpのオフセットとは関係ないことに注意してください。ただし、Portal 2のコンソール版に関しては例外で、そこではfileofsはGamelumpからの相対的なオフセットです。

Static Prop

興味深いのは、「scrp」(ASCII表記、10進数で1936749168)というGamelump IDを用いるprop_staticエンティティの格納に使われるGamelumpです。他のほとんどのエンティティとは異なり、Static PropはエンティティLumpに格納されません。Sourceで用いるGamelumpの形式はpublic/gamebspfile.hで定義されています。

Static Prop Gamelumpの最初の要素は辞書で、int値のカウントに続いてマップで使用されるモデル(Prop)の名前のリストがあります。

struct StaticPropDictLump_t
{
	int	dictEntries;
	char	name[dictEntries];	// モデル名
};

nameの要素はそれぞれ128文字で、ヌル文字でこの文字数まで埋められています。

辞書に続くのはリーフ配列です。

struct StaticPropLeafLump_t
{
	int leafEntries;
	unsigned short	leaf[leafEntries];
};

おそらくこの配列は各Propが配置されているリーフを見つけるためにリーフLumpへインデックスをつけるために使用されます。また、prop_staticはいくつかのリーフにまたがって配置される場合があります。

次に、StaticPropLump_t構造体の数を示すint値に続いてその構造体が多く続きます。

struct StaticPropLump_t
{
	// v4
	Vector		Origin;		 // 原点座標
	QAngle		Angles;		 // 方向(ピッチ ロール ヨー)
	unsigned short	PropType;	 // モデル名の辞書へのインデックス
	unsigned short	FirstLeaf;	 // リーフ配列へのインデックス
	unsigned short	LeafCount;
	unsigned char	Solid;		 // 固体性(?)の種類
	unsigned char	Flags;
	int		Skin;		 // モデルスキン番号
	float		FadeMinDist;
	float		FadeMaxDist;
	Vector		LightingOrigin;  // ライティング用
	// v5から
	float		ForcedFadeScale; // フェード距離スケール
	// v6とv7のみ
	unsigned short  MinDXLevel;      // 見えるための最低DirectXバージョン
	unsigned short  MaxDXLevel;      // 見えるための最大DirectXバージョン
        // v8から
	unsigned char   MinCPULevel;
	unsigned char   MaxCPULevel;
	unsigned char   MinGPULevel;
	unsigned char   MaxGPULevel;
        // v7から
        color32         DiffuseModulation; // インスタンスごとの、色とアルファのモジュレーション
        // v10から
        float           unknown; 
        // v9から
        bool            DisableX360;     // trueならXbox360で表示しない
};

Propの座標はOriginメンバによって、向き(ピッチ ロール ヨー)は3つのfloat型ベクトルであるAnglesで与えられます。PropTypeは上で与えられたPropモデル名の辞書へのインデックスです。他の要素はマップのBSP構造におけるPropの位置、ライティング、Hammerで設定された他のエンティティプロパティに対応します。Gamelumpの指定されたバージョン(dgamelump_t.versionを参照)が十分に高い場合は、さらに要素が存在します(ForcedFadeScaleなど)。バージョン4と5のStatic Prop GamelumpはHL2の公式マップで使用されています。TF2からはバージョン6が現れました。Left 4 Deadのマップの一部ではバージョン7が使用され、Zeno Clashのマップでは変更が加えられたバージョン7が使用されています。バージョン8は主にLeft 4 Deadで使用され、バージョン9はLeft 4 Dead 2で使用されます。Tactical Interventionでは新しいバージョンであるバージョン10が現れます。

その他

Source BSPファイルで使われる他のGamelumpは、Detail prop gamelump(dprp)、Detail prop lighting gamelump(LDRはdplt、HDRはdplh)です。これらはDisplacementに特定のテクスチャを指定した場合に自動的に現れるprop_detailエンティティ(草むらなど)に使用されます。

Gamelumpのサイズには特に制限はないようです。

Displacement

DisplacementサーフェスはBSPファイルの中で最も複雑な部分であり、ここで説明されるのはそのフォーマットの一部のみです。DisplacementのデータはいくつかのデータLumpに分割されていますが、それらの基本的な参照はDispInfo Lump (Lump 26) によるものです。DispInfoは面、元の面、ブラシ側面の配列から参照されます。

DispInfo

struct ddispinfo_t
{
	Vector			startPosition;		// 方向付けのために用いられる開始位置
	int			DispVertStart;		// LUMP_DISP_VERTSへのインデックス
	int			DispTriStart;		// LUMP_DISP_TRISへのインデックス
	int			power;			// サーフェスのサイズを示す(2^power - 1)
	int			minTess;		// 許容される最小のテセレーション(?)
	float			smoothingAngle;		// ライティング スムージング角度
	int			contents;		// サーフェス コンテンツ
	unsigned short		MapFace;		// このDisplacementがどの面から得られたかを示すインデックス
	int			LightmapAlphaStart;	// ddisplightmapalphaへのインデックス
	int			LightmapSamplePositionStart;	// LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONSへのインデックス
	CDispNeighbor		EdgeNeighbors[4];	// NEIGHBOREDGE_ の定義によりインデックスされる
	CDispCornerNeighbors	CornerNeighbors[4];	// CORNER_ の定義によりインデックスされる
	unsigned int		AllowedVerts[10];	// アクティブな頂点(?)
};

この構造体は176バイトの長さを持ちます。startPositionはDisplacementの最初のコーナーの座標です。DispVertStartDispTriStartはDispVerts LumpとDispTris Lumpへのインデックスです。powerはDisplacementの分割数を表します。許容値は2、3、4で、これらの値はDisplacementの各辺を4,8、16本に分割することに対応しています。この構造体はEdgeNeighborsおよびCornerNeighborsメンバを介してこのDisplacementの側面や角に隣接するDisplacementも参照します。隣接するDisplacementの順序には複雑な規則があります。詳細はbspfile.hのコメントを参照してください。MapFaceは面の配列へのインデックスで、このDisplacementに変換される元になった面です。この面にはテクスチャ、Displacement全体の物理的位置、Displacementの境界を設定するために使用されます。

DispVerts

DispVerts Lump (Lump 33) にはDisplacementの頂点データが含まれていて、次のように与えられます。

struct dDispVert
{
	Vector	vec;	// Displacementのボリュームを定めるベクトル場
	float	dist;	// Displacement距離.
	float	alpha;	// 「頂点ごとの」アルファ値
};

vecはDisplacementの各頂点について、元の(平坦な)位置からの変位を表すベクトルを正規化したものです。distは変位の距離です。alphaはその頂点でのテクスチャのアルファブレンド値です。

powerpのDisplacementは、DispVertStartから(2p + 1)2個のDispVertsを参照します。

DispTris

DispTris Lump (Lump 48) にはDisplacementのメッシュの特定の三角形のプロパティに関する「三角形タグ」もしくはフラグが含まれています。

struct dDispTri
{
	unsigned short Tags;	// Displacementの三角形タグ
};

フラグが示すものは以下の通りです。

名前
DISPTRI_TAG_SURFACE 0x1
DISPTRI_TAG_WALKABLE 0x2
DISPTRI_TAG_BUILDABLE 0x4
DISPTRI_FLAG_SURFPROP1 0x8
DISPTRI_FLAG_SURFPROP2 0x10

powerpのDisplacementには2×(2p)2個のDispTrisがあります。それらはおそらくその位置が歩行可能かどうかなど、Displacementを構成する各三角形のプロパティを示すために使用されます。

DispInfoは1つのマップにつき2048個の制限があり、DispVertsとDispTrisの制限は2048個すべてのDisplacementのpowerが4である場合の個数に制限されています(つまり、もっとも細かく分割された場合の個数です)。

Displacementに関連する他のデータは、DispLightmapAlphas Lump (Lump 32) とDispLightmapSamplePos Lump (Lump 34) であり、Displacementのライティングに関連していると思われます。

Pakfile

Pakfile Lump (Lump 40) はBSPファイルに埋め込まれた複数のファイルを格納できる特別なLumpです。通常、マップ内のenv_cubemapエンティティからの反射マップを格納するための特別なテクスチャ(.vtf)ファイルとマテリアル(.vmt)ファイルが含まれています。これらのファイルはbuildcubemapsコンソールコマンドが実行された時にビルドされてPakfile Lumpに格納されます。Pakfileにはマップで使用されるカスタムテクスチャやPropのようなものも任意で含めることができ、それらはBSPZIPプログラム(もしくはPakratのような代替プログラム)を用いてBSPファイル内に配置されます。また、これらのファイルはゲームエンジンのファイルシステムに統合されて、外部のファイルが使用される前に優先的に読み込まれます。

Pakfile Lumpの形式は、圧縮が指定されていない場合(つまり、個々のファイルが非圧縮形式で保存されている場合)はZip圧縮ユーティリティで用いられる形式と同じです。Pakfile Lumpを展開すると、WinZipなどのプログラムで開くことができるようになります。

ヘッダファイルpublic/zip_uncompressed.hは、Pakfile Lumpに存在する構造体を定義します。Lumpの最後の要素はZIP_EndOfCentralDirRecord構造体です。これはその構造体の直前に、Pakに存在する各ファイルに対して1つずつあるZIP_FileHeader構造体の配列を指します。これらのヘッダはそれぞれ、ファイルのデータが後ろに続いているZIP_LocalFileHeader構造体を指し示します。

Pakfile Lumpは通常、BSPファイルの最後の要素です。

Cubemap

Cubemap Lump (Lump 42) は16バイトのdcubemapsample_t構造体の配列です。

struct dcubemapsample_t
{
	int		origin[3];	// 最も近い整数に丸められたライトの位置
	int	        size;		// Cubemapの解像度(0: デフォルト)
};

dcubemapsample_t構造体は、マップ内のenv_cubemapエンティティの場所を定義します。originメンバには、Cubemapの整数座標x、y、zが含まれ、sizeメンバは2size - 1ピクセルの正方形として指定されるCubemapの解像度で、0の場合はデフォルトのサイズである6(32×32ピクセル)になります。ファイルには最大1024個のCubemapが存在します(MAX_MAP_CUBEMAPSAMPLES)。

buildcubemapコンソールコマンドが実行されると、各Cubemapエンティティの位置でマップのスナップショットが6つ(各方向について1つずつ)撮影されます。これらのスナップショットはマルチフレームテクスチャファイル(.vtf)に格納され、Pakfile Lump(上記)に追加されます。テクスチャ名はcX_Y_Z.vtfで、(X, Y, Z)はCubemapの(整数)座標です。

環境マッピングされたマテリアルを含む面(例えば光沢のあるテクスチャ)は、マテリアル名を介してCubemapを参照します。(例えば)walls/shiny.vmtと名前の付いたマテリアルは書き換えられて(新しいTexinfoとTexdataが作成されて)、変更されたマテリアル名であるmaps/mapname/walls/shiny_X_Y_Z.vmtを参照するようになります。ここで(X、Y、Z)は 前述したようにCubemapの座標です。この.vmtファイルはPakfileにも格納され、$envmapプロパティを使用してCubemapの.vtfファイルを参照します。

バージョン20のファイルにはさらにcX_Y_Z_hdr.vtfがPakfile Lumpに追加されます。これにはRGBA16161616F形式(チャンネルごとに16ビット)のHDRテクスチャファイルが含まれています。

オーバーレイ

単純なデカール(infodecalエンティティ)とは異なり、info_overlayはエンティティLumpから削除されて、オーバーレイLump (Lump 45) に分けて保存されます。この構造体はHammerのエンティティのプロパティをほぼ正確に反映しています。

struct doverlay_t
{
	int		Id;
	short		TexInfo;
	unsigned short	FaceCountAndRenderOrder;
	int		Ofaces[OVERLAY_BSP_FACE_COUNT];
	float		U[2];
	float		V[2];
	Vector		UVPoints[4];
	Vector		Origin;
	Vector		BasisNormal;
};

FaceCountAndRenderOrderメンバは2つの部分に分かれています。下位14ビットはオーバーレイが表示される面の数で、上位2ビットはオーバーレイの表示順序です(重なったオーバーレイの場合)。Ofacesは要素数64の配列で(OVERLAY_BSP_FACE_COUNT)、オーバーレイが表示される面へのインデックスが格納されています。他の要素はオーバーレイのテクスチャ、スケール、向きを設定します。1つのファイルには最大512個のオーバーレイが存在します(MAX_MAP_OVERLAYS)。また、Dota 2ではオーバーレイ数の制限が大幅に増加しています。

ライティング

ライティングLump (Lump 8) はマップの面の静的ライトマップサンプルを格納するために使用されます。各ライトマップサンプルは、テクスチャピクセルの色と乗算する色合いであり、様々な強度の照明を生成します。これらのライトマップはマップコンパイル時のVRAD処理中に作成され、dface_t構造体から参照されます。現在のライティングLumpのバージョンは1です。

dface_tではstyles[]配列で定義された最大4つのライトスタイルを持つことができます(ただし値255はライトスタイルがないことを示す)。面の各方向についてのルクセル数は、2つのLightmapTextureSizeInLuxels[]メンバの値(+ 1)によって与えられ、面ごとのルクセルの総和は次のようになります。

(LightmapTextureSizeInLuxels[0] + 1) * (LightmapTextureSizeInLuxels[1] + 1)

面はそれぞれ、dface_tlightofsメンバによってライティングLump内のオフセットを持ちます(もし、スカイボックスやnodrawなどの不可視のテクスチャであるなどの理由でその面にライティング情報が使われていない場合、lightofsは-1です)。ライトマップサンプルの総数は(ライトスタイル数)×(ルクセル数)で、各サンプルはColorRGBExp32構造体で与えられます。

struct ColorRGBExp32
{
	byte r, g, b;
	signed char exponent;
};

この構造体から、それぞれの色成分に2exponentをかけることで標準のRGB形式を得ることができます。バンプマップ付きのテクスチャを持つ面の場合、おそらくバンプマップを計算するためのサンプルを含むためライトマップサンプルの数は通常の4倍になります。

lightofsで参照されるサンプルグループの直前には、面のライティングの平均値がライトスタイルごとに、styles[]配列で与えられた順番とは逆の順番になって存在しています。

バージョン20のBSPファイルには、同じサイズの2つ目のライティングLump (Lump 53) が含まれています。これは、各ライトマップサンプルに対してより正確な(より高精度の)HDRデータを格納するものと推定されています。フォーマットは現在不明ですが、1サンプルにつき32ビットです。

ライティングLumpの最大サイズは0x1000000バイトです(MAX_MAP_LIGHTING)。すなわち、16MBです。

アンビエントライティング

アンビエントライティングLump (Lump 55とLump 56) は、BSPバージョン20以降に存在します。Lump 55はHDRライティングに使用され、Lump 56はLDRライティングに使用されます。これらのLumpは、Volumetric Ambient Lighting情報(例えば、NPC、ビューモデル、静的でないPropなどのエンティティのためのライティング情報)を格納するために使われます。バージョン20より前はこのデータをリーフLumpのdleaf_t構造体に格納していましたが、この新しいLumpよりもはるかに低い精度でした。

アンビエントライティングLumpは両方ともdleafambientlighting_t構造体の配列です。

struct dleafambientlighting_t
{
	CompressedLightCube	cube;
	byte x;		// 固定小数点で、リーフのバウンディングボックスの割合
	byte y;		// 固定小数点で、リーフのバウンディングボックスの割合
	byte z;		// 固定小数点で、リーフのバウンディングボックスの割合
	byte pad;	// 未使用
};

各リーフはdleafambientlighting_t構造体のうちのいくつかに関連付けられています。各構造体はxyzメンバによって指定された位置にある周囲光データのキューブを含みます。これらの座標はリーフが持つバウンディングボックスの割合として格納されます。つまり、xが0の場合はリーフの最西端で、255の場合は最東端、128の場合は中心を表します。

各サンプルのライティングデータは、CompressedLightCube構造体で表されます。これは、前のセクションで説明したColorRGBExp32構造体が6つ格納された配列です。

struct CompressedLightCube
{
	ColorRGBExp32 m_Color[6];
};

配列中の各ライティングサンプルは、3D空間内の各座標軸方向から受ける光の量に対応します。

コンパイル時に、VRADは各リーフで周囲光のサンプルを取る位置ランダムに生成し、各サンプル点についてのライティング情報をdleafambientlighting_t構造体へ格納します。各リーフと周囲光のサンプルを関連付けるために、アンビエントライティングインデックスLump (Lump 51とLump 32) が用いられます。Lump 51はHDRについてのアンビエントライティングインデックス情報を、Lump 52はLDRについての情報を格納します。

アンビエントライティングインデックスLumpはdleafambientindex_t構造体の配列です。

struct dleafambientindex_t
{
	unsigned short ambientSampleCount;
	unsigned short firstAmbientSample;
};

この配列のN番目のdleafambientindex_t構造体は常にdleaf_t配列のN番目に対応します。ambientSampleCountフィールドは対応するリーフに関連する周囲光の数で、firstAmbientSampleはアンビエントライティング配列へのインデックスで、これは関連付けられたリーフの最初の周囲光サンプルを参照します。

オクルージョン

オクルージョンLump (Lump 9) にはポリゴンジオメトリとfunc_occluderエンティティで使用されるいくつかのフラグが含まれています。他のブラシエンティティとは異なり、func_occluderはエンティティLumpで「model」キーを使用しません。代わりに、そのブラシはコンパイル処理中にエンティティから分離され、occludernumという数値としてオクルーダーキーが割り当てられます。tools/[toolsoccluder]またはtools/[toolstrigger]のテクスチャが付いたブラシの側面は、オクルーダーキーとともに保存され、このLumpにいくつかの追加情報が格納されます。

このLumpは3つに分割され、オクルーダーの総数に、一定のサイズのdoccluderdata_tフィールドの配列が続いたものから始まります。次のパートは別の整数値で始まり、こちらはオクルーダーの総ポリゴン数です。続いてその数だけdoccluderpolydata_tフィールドの配列が続きます。3つ目のパートはオクルーダーの総頂点数を表す整数から始まり、頂点インデックスが続きます。

struct doccluder_t
{
	int			count;
	doccluderdata_t		data[count];
	int			polyDataCount;
	doccluderpolydata_t	polyData[polyDataCount];
	int			vertexIndexCount;
	int			vertexIndices[vertexIndexCount];
};

doccluderdata_t構造体にはオクルーダーのフラグと寸法、そしてその領域が含まれています。firstpolypolycountを含むdoccluderpolydata_tへの最初のインデックスです。

struct doccluderdata_t
{
	int	flags;
	int	firstpoly;	// doccluderpolysへのインデックス
	int	polycount;	// ポリゴンの数
	Vector	mins;	        // 全頂点の最小値
	Vector	maxs;	        // 全頂点の最大値
	// v1から
	int	area;
};

オクルーダーポリゴンはdoccluderpolydata_t構造体に格納されていて、firstvertexindexフィールドを含みます。これはオクルーダーの頂点配列へのインデックスで、オクルーダーの頂点配列の要素は頂点Lump (Lump 3) の配列へのインデックスとなっています。頂点インデックスの総数はvertexcountに格納されます。

struct doccluderpolydata_t
{
	int	firstvertexindex;	// doccludervertindicesへのインデックス
	int	vertexcount;		// 頂点インデックスの数
	int	planenum;
};

Physics

Physcolldie Lump (Lump 29) にはワールドの物理的なデータが含まれています。

このLumpはひと続きのmodelで構成されていて、各modelは以下から構成されます。

  • dphysmodel_tヘッダ
     struct dphysmodel_t
     {
     	int modelIndex;  // おそらくこの物理モデルを適用するモデルへのインデックス?
     	int dataSize;    // コリジョンデータセクションの合計サイズ
     	int keydataSize; // テキストセクションのサイズ
     	int solidCount;  // コリジョンデータセクションの数
     };
  • 一連のコリジョンデータセクション(compactsurfaceheader_tを含む)
  • テキストセクション

このLumpはmodelIndexが-1に設定されたdphysmodel_t構造体で終了します。

最後の2つの部分は、PHYファイル形式と同じに見えます。すなわち、正確な内容は不明です。compactsurfaceheader_t構造体には各コリジョンデータセクション(ヘッダの残りも含む)のサイズが含まれているので、このLumpは次のように解析できます。

   while(true) {
       header = readHeader();
       if(header.modelIndex == -1)
           break;
       
       for(int k = 0; k < header.solidCount; k++) {
           size = read4ByteInt();
           collisionData = readBytes(size);
       }
       
       textData = readBytes(header.keydataSize);
   }

その他

To do: これらの情報の一部は推測に基づくものである可能性があるため、更なる調査が必要です。

  • Worldlights Lump (Lump 15) にはワールドにあるそれぞれのスタティックライトエンティティに関する情報が含まれていて、移動するエンティティの半動的ライティングを提供するために使用されているようです。
  • Area Lump (Lump 20) は、Areaportal Lump (Lump 21) を参照し、func_areaportalおよびfunc_areaportalwindowエンティティとともに使用することでマップをレンダリングするかしないかを切り替えられる仕切りを定義します。
  • Portal Lump (Lump 22) 、Cluster Lump (Lump 23) 、PortalVerts Lump (Lump 24) 、ClusterPortals (Lump 25) 、ClipPortalVerts (Lump 41) はコンパイルのVVIS処理でどのクラスタがあるクラスタから見ることができるかを確認するために使用されます。クラスタはマップ内のプレイヤーが進入可能なリーフ ボリュームです(上記)。「ポータル」はクラスタまたはリーフが隣接する部分のポリゴン境界のことです。この情報の大部分はVRADプログラムによってスタティックライティングを計算するためにも用いられ、その後はBSPファイルから削除されます。
  • PhysCollide Lump (Lump 29) とPhysCollideSurface Lump (Lump 49) はゲームエンジンでエンティティの衝突に関する物理シミュレーションに関連しているようです。
  • VertNormal Lump (Lump 30) とVertNormalIndices Lump (Lump 31) は面のライトマップのスムージングに関連している可能性があります。
  • FaceMacroTextureInfo Lump (Lump 47) は、マップ内の面の数と同じ数の要素を持ったshort値の配列です。この要素に-1(0xFFFF)以外のものが含まれている場合、その面はTexDataStringTableのテクスチャ名へのインデックスを持っています。VRADでは、対応するテクスチャはワールドエクステント(?)にマッピングされ、その面のライトマップのモジュレーションとして使用されます。すべての面に適用されるベースマクロテクスチャ(materials/macro/mapname/base.vtfに位置する)が見つかることもあります。VTMBのマップだけがマクロテクスチャを使用しているようです。
  • LeafWaterData Lump (Lump 36) とLeafMinDistToWater Lump (Lump 46) は、水のボリュームに関してプレイヤーの位置を決定するために用いられるようです。
  • Primitives Lump (Lump 37) とPrimVerts Lump (Lump 38) は、「非ポリゴンプリミティブ」に関することに使用されます。これらはもともと水のメッシュを分割するためだけに用いられていたため、SDK Sourceでは「waterstrips」、「waterverts」、「waterindices」と呼ばれることもあります。現在は、面を構成する辺に「T字型接合」(2つの頂点からなる直線上に頂点がある状態)が含まれる場合に隣接する面との間にクラックが発生するのを防ぐために使用されています。PrimIndices Lumpは面の頂点間の三角形のセットを定義して、面をテセレーションします。そして、それらはPrimitives Lumpから参照されます。Primitive Lumpは面Lumpによって参照されます。現在のマップでは、PrimVerts Lumpは全く使用されていないようです(参考)。
  • HDRライティング情報を含んでいるバージョン20のファイルは、さらに4つの追加のLumpを持っていますが、現在その内容は正確には分かっていません。Lump 53は常に標準のライティングLump (Lump 8) と同じサイズであり、おそらく各ライトマップサンプルについて精度の高いデータを含んでいます。Lump 54はWorldlight Lump (Lump 15) と同じサイズであり、おそらくライトエンティティにおけるHDR関連のデータを含みます。

About

Source BSP File Formatを日本語訳したもの(Google 翻訳 + 分かりやすく)

https://www.patreon.com/ZenkakuHiragana

License:MIT License