Szerintem nem létezik olyan játékos, akinek az emlékei nyomokban q3 vagy q3 engine-nel készült játékkal kapcsolatos élményeket ne tartalmaznának. Joggal nevezhetjük az id software és talán a játéktörténelem egyik legsikeresebb játékának ill. engine-jének hiszen még ma is akadnak lelkes rajongók szép számmal.
A mitől vált ilyen sikeressé kérdésre mindenki megkeresheti a neki tetsző választ, de egy biztos, hogy a játék engine-nek nagy szerep jutott ebben, amelynek alappillérei a jelenet-,tér- és megjelenítési-fa. A .bsp file a legtöbb ezekhez kapcsolódó információt tárolja így hát érdemes alaposan beleásnunk magukat a részleteibe.
A .bsp fa 17-féle információt tárol valamint egy headert. Ezek nagy része grafikai (renderelési) és ütközésvizsgálathoz használt információ, a többi bsp fával kapcsolatos és játék specifikus információ.
Kezdjük ez utóbbival, mivel ez a legegyszerűbb, de a feldolgozása ennek a legkomplikáltabb lévén szöveges adatról van szó, amelyet egy script-nyelven írtak. Ennek neve entities.
A bsp fával kapcsolatos információ egy láthatósági tömb (vis_info). A file tárolja még a teret felosztó síkokat (planes), a bsp fa csomópontjait (nodes), leveleit (leaves) és a levelekhez tartozó felületek indexeit (leaf_surfaces) és a levelekhez tartozó brushok indexeit (leaf_brushes).
A grafikai adatokhoz tartoznak a vertexek (vertices), a felületek (surfaces) és a vertexindexek (indices) ill. még a modelek (model). A renderelési adatokhoz leginkább a lightmapek (lightmaps), a fényrács (lightgrid), a shaderek (shaders) és köd effektek (fogs).
Az ütközésvizsgálathoz szükséges adatok a brushes és a brushsides.
Mindenekelőtt néhány szösszenet arról, hogy mi mire való.
A shadereknek természetesen semmi közük a mai gpu shaderekhez ellenben a felületek megjelenítésére vannak hatással, mint pl. ezek tárolják a textúrákat, textura animációkat, vertex deformációt és egyéb effekteket. A köd effektek és a felületek használják ezeket viszont a bsp ezeket nem tárolja! A q3ban az árnyalás alapvetően statikus jellegű és a felületek előre elkészített árnyalási eredményét a lightmapekben tárolják a bsp file részeként, amely megegyezés szerint (ha más nem utal rá) 128*128 méretű és RGB-ben kódolja a luxeleket (mert az mindig van otthon...). Ahhoz, hogy a mozgó objektumokat megfelelően lehessen árnyalni egy fényrácsban tárolják az adott térfogatban érvényes fényteret.
A modeleket szintén nem tárolja a bsp file, hanem csak brushokat és egy bounding boxot. És hogy mi is az a brush - kissé furcsa az elnevezés hiszen ecsetet jelent miközben ütközés vizsgálathoz használják. Igazából a kettőnek csak annyi a köze egymáshoz, hogy a pályákat mintegy megrajzolják, amihez ecsetet használnak (érted...).
Tapasztalatom szerint a brushokkal ma már nem érdemes foglalkozni, hiszen manapság az ütközés vizsgálatot már egy külön fizikai engine végzi általában (persze tanulási / felfedezési célból nagyon izgalmas lehet egy ilyet implementálni).
Amiről még szó lesz az a láthatósági vizsgálat, amely ún. clustereken alapul. A bsp fa leveleit ilyenekbe szervezi a bsp compiler és a pvs-t ezek között készíti el. Ezenkívül még vannak area-k is, amelyek portálokkal vannak összekötve. Ilyen portálok általában az ajtók. A portálok használatáról azonban nem sokat tudok és azt is q3 forrásából...
Utoljára még egy fontos információ: a vertexindexek egy 4 byte-os integer tömb ill. minden egyéb referencia index is ilyen integer.
Most pedig merüljünk el az adattipusok részletes leírásában....
Elsőként a header:
struct
{
unsigned char id[4];
unsigned int version;
struct
{
int offset;
int length;
}table[17];
};
az id kötelezően "IBSP", a version pedig játék függő. A 17 elemű table tárolja, hogy melyik tipusu adatot hol találjuk a fileban (a file elejétől mérve) és milyen méretű. Azt, hogy egy adatból hány darab található könnyen kiszámolhatjuk, ha a length-t elosztjuk az adott struktura méretével.
A bsp-fához tartozó adatok:
- A vis_info egy pvs vagyis egy meglehetősen nagy mátrix bitekből felépítve, amely azt tárolja, hogy a bsp fa egy adott levelét befoglaló clusterből mely egyéb clusterek (levelek) látszódhatnak (innen ered a neve is potentially visible set).
- A plane egy 4 floattal leírt struktura, amely tárolja a sík normálvektorát(a,b,c) és az origótól mért távolságát(d)
struct
{
float a,b,c;
float d;
}; - A node tárolja melyik sík osztja ketté és melyik 2 gyermekké osztja valamint a bounding boxát.
struct
{
int plane;
int child_a;
int child_b;
int aabb_min[3];
int aabb_max[3];
}; - A leaf tárolja, hogy melyik cluster és portál area tartozik hozzá valamint a bounding boxát és a leafhez tartozó első surface/brush indexét és hogy hány darab surface/brush tartozik hozzá.
struct
{
int cluster;
int area;
int aabb_min[3];
int aabb_max[3];
int first_leafSurface_index;
int num_leafSurfaces;
int first_leafBrush_index;
int num_leafBrushes;
};
Grafikai adatok:
- A vertex tárolja a 3d-s pozicióját, két, 2d-s textura koordinátát (egyik a diffúz maphez, másik a lightmaphez) egy normált és 4 byte-ot, ami egy szín RGBA komponensét jelenti.
struct
{
float position[3];
float texcoord_diffuse[2];
float texcoord_lightmap[2];
float normal[3];
unsigned char color[4];
}; - Talán a felület a legösszetettebb struktura, amelynek ráadásul a jelentése függ a tipusától.
struct
A számunkra lényeges adatok közül az alábbi adatok egyértelműek: shader_index, lightmap_index, fog_index, type. A kavarodást a tipus fogja okozni mivel 4 féle felület tipust (legalábbis ennyiről tudok) támogat a bsp file. Ez lehet sík felület, type = 1, háromszög lista, type = 2, bezier patch, type = 3, billboard avagy flare, type = 4.
{
int shader_index;
int fog_index;
int type;
int first_vertex;
int num_vertex;
int first_vertexIndex;
int num_vertexIndex;
int lightmap_index;
int lightmap_corner[2];
int lightmap_width, light_height;
float lightmap_origin[3];
float tangent[3];
float bitangent[3];
float normal[3];
int patch_width, patch_height;
};
Ezek közül az 1. és 3. eset nagyon hasonló. Mindkét esetben a bsp file vertex tömbjéből num_vertex számú különböző vertexet fogunk felhasználni (nem feltétlen megjeleníteni!) a first_vertextől kezdve. Sík felület esetében itt véget is ér a dolog hiszen num_vertex darab vertexet fogunk megjeleníteni háromszög legyezőként. 3. esetben szükségünk van még a bsp file vertex index tömbjére is, amelyből hasonlóan az előző esethez nyerhetjük ki a vertex indexeket (first_vertexIndex-től indulva num_vertexIndex-nyit) egyszerű háromszög listaként.
Tudomásom szerint a 4. eset egy billboardolt négyzetet jelent, amit a lightmap_origin koordinátákban kell megjeleníteni.
Utoljára hagytam a 2. típust, mert ez igényli a legtöbb átalakítást mivel ez esetben nekünk kell legenerálnunk a vertexeket is. A számoláshoz szükséges kontroll pontokat a bsp vertex tömbje tárolja, a first_vertextől kezdve.
Végszóként még nézzük meg, hogyan is épül fel a fénytér:
struct
{
unsigned char ambient[3];
unsigned char diffuse[3];
unsigned char direction[2];
};
Az itteni leírás csak az eredeti Q3 motorral készült játékok bsp file-jaihoz használható, amely azt jelenti, hogy a headerben a verziószám értéke 0x2E! Érdekesség, hogy a q3map2 alkalmazás a különböző játékok bsp file-jai között képes konvertálni.