NFS3 Track File Formats - V0.04, (c) Denis Auroux 1998-99
This help page describes the structure of NFS3 track files. It is intended as reference information for other NFS3 developers ; if you don‚t intend to program your own track editor, then the NFS3 Tracks Overview page is probably more appropriate.
Some of the information in this document was contributed by Vitaly Kootin.
DISCLAIMER : The information below is unofficial, and may be inexact by some aspects. It is only intended as reference information to help other NFS3 developers, and comes with no warranty. It may not be used for commercial purposes. It is forbidden to distribute a modified version of this document.
Contents :
1. FLOATPT structure (size : 12 bytes)
typedef struct FLOATPT {
float x,z,y;
} FLOATPT;
This structure is used to store the three coordinates of a vertex. Each of the components is stored as a floating-point number (IEEE single-precision format). The x and y axes are horizontal, while the z axis is vertical and points upwards.
2. INTPT structure (size : 12 bytes)
typedef struct INTPT {
long x,z,y;
} INTPT;
This is another way to store the three coordinates of a vertex (less frequently used than the FLOATPT structure). Each of the components is stored as a 32-bit integer, and must be divided by 2^16 in order to obtain the corresponding floating-point value (in other words, the coordinates are 16.16 fixed precision numbers).
3. POLYGONDATA structure (size : 14 bytes)
typedef struct POLYGONDATA {
short vertex[4];
short texture;
short unknown1;
unsigned char flags;
unsigned char unknown2;
} POLYGONDATA;
This structure describes a polygon. The vertex
member refers to four entries in the vertex table associated with the polygon (either the vertex table of the TRKBLOCK structure in the case of a POLYBLOCK polygon, or that of the XOBJDATA structure in the case of an XOBJDATA polygon), describing the coordinates of the four polygon vertices. The texture
member points to one of TEXTUREBLOCK structures in the FRD file and describes the polygon texture. The significance of the unknown1
and unknown2
members apparently has to do with animated textures (resp. first texture number and animation speed ?) or with the special road lane textures. A non-zero value of flags&0x04
indicates that the polygon has an animated texture, and a non-zero value of flags&0x10
indicates that the polygon is two-sided.
1. TRKBLOCK structure (variable size)
typedef struct TRKBLOCK
{
struct FLOATPT ptCentre;
struct FLOATPT ptBounding[4];
long nVertices;
long nHiResVert,nLoResVert,nMedResVert;
long nVerticesDup;
long nObjectVert;
struct FLOATPT vertices[nVertices];
long shadingVertices[nVertices];
struct NEIGHBORDATA nbdData[0x12C];
long nStartPosition;
long nPositions;
long nPolygons,nVroad,nXobj,nPolyobj;
long nSoundsrc,nLightsrc;
struct POSITIONDATA posData[nPositions];
struct POLYVROADDATA polyData[nPolygons];
struct VROADDATA vroadData[nVroad];
struct REFXOBJ xobj[nXobj];
struct REFPOLYOBJ polyobj[nPolyobj];
char padding[];
struct SOUNDSRC soundsrc[nSoundsrc];
struct LIGHTSRC lightsrc[nLightsrc];
} TRKBLOCK;
This structure describes a track block ; it contains the track vertices, virtual road information, and miscellaneous reference tables, but does not include the track polygons nor the objects. The ptCentre
member provides the coordinates of a central reference point around which the block is built. The ptBounding
member provides the coordinates of four bounding points for the block vertices : these points delimit a horizontal rectangle inside which all the block‚s elements fit.
The nVertices
and nVerticesDup
values are always equal, and give the total number of vertices stored in the vertices
member of the TRKBLOCK structure. Vertices 0
to nObjectVert-1
are the vertices used by the polygon objects (see OBJPOLYBLOCK structure). Vertices nObjectVert
to nLoResVert-1
are used in chunks 0 and 1 of the POLYBLOCK structure, and correspond to the track vertices in low resolution. Vertices nLoResVert
to nMedResVert-1
are the additional vertices used in chunks 2 and 3 of the POLYBLOCK structure, and correspond to the track vertices in medium resolution (the low-resolution vertices are also used in medium resolution). Vertices nMedResVert
to nHiResVert-1
are the additional vertices used in chunks 4 and 5 of the POLYBLOCK structure, and correspond to the track vertices in high resolution (the low- and medium-resolution vertices are also used in high resolution). Finally, vertices nHiResVert
to nVertices-1
are used in chunk 6 of the POLYBLOCK structure, and correspond to the road lanes ; after each road lane, a hole is usually left with four unreferenced vertices. The shadingVertices
member describes the shading applied to each vertex : for each vertex, the least significant byte is the blue component, the next byte is the green component, the next byte is the red component, and finally the most significant byte is the alpha value.
The nStartPosition
member indicates where the block is located along the track, in units of "nodes" (see the COLVROAD structure in COL file). Normally each block corresponds to 8 node positions, except the last one : the number of nodes in the block is given by the nPositions
member. The nPolygons
member indicates the total number of high-resolution track polygons (i.e. the same as the size of chunk 4 of the POLYBLOCK structure). The nXobj
member indicates the total number of non-animated (type 4) extra-objects stored in the four extra-object chunks associated with the track block. The nPolyobj
member indicates the total number of objects (both polygon-objects and extra-objects) in object chunk 0 (this normally corresponds to length of OBJPOLYBLOCK chunk 0 in the POLYBLOCK structure, except if there are only extra-objects).
In addition, the nPositions
, nPolygons
, nVroad
, nXobj
, nPolyobj
, nSoundsrc
and nLightsrc
members describe the number of entries in the various corresponding substructures (see below). As the REFPOLYOBJ structure exists in two variants (16-byte or 20-byte long depending on object type), extra padding is added in the .FRD file just after the polyobj
substructure, in order to obtain a total size of 20*nPolyobj
in all cases for the REFPOLYOBJ table. Apart from this there is no padding anywhere.
2. NEIGHBORDATA structure (size : 4 bytes)
typedef struct NEIGHBORDATA {
short block;
short unused;
} NEIGHBORDATA;
This structure is used to list the block numbers corresponding to the neighbors of the currently described block, starting with the closest ones. There are always 0x12C such entries in the TRKBLOCK structure, but for obvious reasons most of these entries are unused and just contain -1 in the block
member.
3. POSITIONDATA structure (size : 8 bytes)
typedef struct POSITIONDATA {
short polygon;
unsigned char nPolygons;
char unknown;
short extraNeighbor[2];
} POSITIONDATA;
This structure is used to indicate which track polygons are attached to a given node. The polygon
member refers to an entry in the POLYVROADDATA structure (or in chunk 4 of the POLYBLOCK structure) ; all entries between this one and the one pointed to in the following POSITIONDATA structure correspond to the high-resolution track polygons attached to the same node. The nPolygons
member indicates the number of polygons attached to this node. The two extraNeighbor
values are usually equal to -1, but can be used to specify up to two neighbouring nodes other than the previous node and the following node (e.g. if there are shortcuts).
4. POLYVROADDATA structure (size : 8 bytes)
typedef struct POLYVROADDATA {
unsigned char vroadEntry;
unsigned char flags;
unsigned char unknown[6];
} POLYVROADDATA;
This structure describes the virtual road properties of a track polygon : each entry corresponds to a polygon in chunk 4 of the POLYBLOCK structure (high-resolution track polygons). The vroadEntry
member refers to a VROADDATA structure which describes the virtual road vectors associated with the polygon.
The flags
member describes the behavior of the polygon : a non-zero value of flags&0x80
indicates that collisions with walls should be handled, i.e. that some of the neighboring polygons are not passable. A non-zero value of flags&0x40
indicates that collisions with extra-objects should be handled, i.e. that some extra-objects might be present over the polygon. The next two bits in flags
are unused. Finally, flags&0x0f
describes the behavior of the polygon when driven over : values of 0 and 14 correspond to a polygon over which cars cannot pass ; a value of 2 corresponds to a polygon which emits gravel when driven over ; values 4 and 11 correspond to emission of leaves, 5 and 13 to emission of dust, 9 and 15 to emission of snow. All other values correspond to plain passable polygons.
5. VROADDATA structure (size : 12 bytes)
typedef struct VROADDATA {
short xNorm,zNorm,yNorm;
short xForw,zForw,yForw;
} VROADDATA;
This structure describes the virtual road vectors associated with a track polygon ; several track polygons can share the same VROADDATA structure. The xNorm,zNorm,yNorm
members describe the coordinates of the normal vector (normalized to length 2^15) while the xForw,zForw,yForw
members contain the coordinates of the forward vector (normalized to length 2^15).
6. REFXOBJ structure (size : 20 bytes)
typedef struct REFXOBJ {
struct INTPT pt;
short unknown1;
short globalno;
short unknown2;
char crossindex;
char unknown3;
} REFXOBJ;
This structure lists the properties of an extra-object. There is one entry for each non-animated (type 4) extra-object, regardless of the chunk to which it belongs ; there are no entries for animated objects. The pt
member contains the coordinates of the object‚s reference point (see XOBJDATA structure). The globalno
member is a unique sequence number characterizing the object among all of the track‚s extra-objects. The crossindex
member indicates the position of the extra-object in the first POLYOBJ chunk ; it is equal to 0 when the object does not belong to the first chunk.
7. REFPOLYOBJ structure (size : 16 or 20 bytes)
This structure, which lists the properties of an object, exists in two versions : the version for polygon objects is 16-bytes long and has the following structure :
typedef struct REFPOLYOBJ {
short entrysize;
char type;
char no;
struct INTPT pt;
} REFPOLYOBJ;
For polygon objects, the entrysize
member equals 16 and the type
member equals 1. The no
member is a serial number among the block‚s objects ; it is apparently not used by NFS3. The used numbers are not consecutive because the four POLYOBJ chunks are interleaved and only the first chunk is described by the REFPOLYOBJ structures. The pt
member contains the reference position of the object.
In the case of extra-objects, the structure is 20-bytes long :
typedef struct REFPOLYOBJ {
short entrysize;
char type;
char no;
struct INTPT pt;
long crossindex;
} REFPOLYOBJ;
The entrysize
member is now equal to 20, and the type
member equals 4 (there are no animated extra-objects in the first chunk). The added crossindex
member indicates the number of the corresponding REFXOBJ structure.
There is one REFPOLYOBJ structure (of either type) for every object (either polygon object or extra-object) in the first POLYOBJ chunk. The three other POLYOBJ chunks are not listed. Also note that, unlike the POLYOBJ chunk, this structure remains present even when there are only extra-objects. The various REFPOLYOBJ structures of a track block are stored consecutively in the .FRD file ; the .FRD files contains padding just after the REFPOLYOBJ structures, in order to obtain a total size of 20*nPolyobj
in all cases for the REFPOLYOBJ table.
8. SOUNDSRC structure (size : 16 bytes)
typedef struct SOUNDSRC {
struct INTPT refpoint;
long type;
} SOUNDSRC;
This structure describes the position and type of a sound source (water stream, etc...).
9. LIGHTSRC structure (size : 16 bytes)
typedef struct LIGHTSRC {
struct INTPT refpoint;
long type;
} LIGHTSRC;
This structure describes the position and type of a light source (special lighting effect).
1. POLYGONBLOCK structure (variable size)
typedef struct POLYGONBLOCK {
struct POLYGONCHUNK poly[7];
struct OBJPOLYBLOCK obj[4];
} POLYGONBLOCK;
This structure lists the polygons making up a track block ; the vertex
members of the contained POLYGONDATA structures refer to the vertex table of the corresponding TRKBLOCK. The POLYGONBLOCK structure contains seven POLYGONCHUNK substructures listing the track polygons, while the polygon objects are stored in the four OBJPOLYBLOCK substructures. Polygon chunk 0 lists the low-resolution track polygons ; chunk 2 lists the medium-resolution track polygons ; chunk 4 lists the high-resolution track polygons ; and chunk 6 lists the polygons making up the lines between road lanes. Chunks 1, 3 and 5 are unused in most cases ; occasionally they contain polygons making up fences and other transparent stuff : chunk 1 corresponds to low resolution, chunk 3 to medium resolution, and chunk 5 to high resolution. Also note that the OBJPOLYBLOCK substructures are non-empty only if there are polygon objects ; if there are only extra-objects in an object chunk, the OBJPOLYBLOCK structure is left empty (the first object chunk is anyway described in the TRKBLOCK structure).
2. POLYGONCHUNK structure (variable size)
typedef struct POLYGONCHUNK {
long sz,szdup;
struct POLYGONDATA poly[sz];
} POLYGONCHUNK;
The sz
member lists the number of polygons in the polygon chunk ; if it equals zero, then only the sz
member is stored and the structure is only 4 bytes long. If sz
is non-zero, its value is duplicated in the szdup
member. The poly
member then lists the polygons themselves.
3. OBJPOLYBLOCK structure (variable size)
typedef struct OBJPOLYBLOCK {
long nPolygons;
long nObjects;
struct POLYOBJDATA obj[nObjects];
} OBJPOLYBLOCK;
The nPolygons
member contains the total number of polygons referenced in the various polygon objects of the OBJPOLYBLOCK structure. If it equals zero, then only the nPolygons
member is stored and the structure is only 4 bytes long. This is in particular the case when all objects are extra-objects. If nPolygons
is non-zero, the nObjects
member indicates the total number of objects in the chunk (both polygon objects and extra-objects), and the objects themselves are stored in the obj
substructure.
4. POLYOBJDATA structure (variable size)
This structure exists in two different versions. In the case of extra-objects, the structure is 4-bytes long :
typedef struct POLYOBJDATA {
long type;
} POLYOBJDATA;
The type
member equals 4 for a basic extra-object, and 3 for an animated extra-object. Since extra-objects are stored in separate XOBJDATA structures, the POLYOBJDATA structure contains no other information in that case. For polygon objects, the structure contains more information :
typedef struct POLYOBJDATA {
long type;
long numpoly;
struct POLYGONDATA poly[numpoly];
} POLYOBJDATA;
The type
member equals 1 for a polygon object. The numpoly
member lists the number of polygons in the object, and the polygons themselves are stored in the poly
substructure.
1. XOBJBLOCK structure (variable size)
typedef struct XOBJBLOCK {
long nobj;
struct XOBJDATA obj[nobj];
} XOBJBLOCK;
This structure is used to store extra-objects. There are actually four XOBJBLOCKs for every track block, each corresponding to one of the four object chunks. The FRD file also contains an extra XOBJBLOCK devoted to global animated extra-objects.
2. XOBJDATA structure (variable size)
This structure is used to describe an extra-object ; it exists in two different versions, one for static objects (type 4) and one for animated objects (type 3). In the case of static objects, the structure is the following :
typedef struct XOBJDATA {
long type;
long crossno;
long unknown;
struct FLOATPT ptRef;
long unknown2;
long nVertices;
struct FLOATPT vert[nVertices];
long shadingVertices[nVertices];
long nPolygons;
struct POLYGONDATA polyData[nPolygons];
} XOBJDATA;
The type
member equals 4 for static extra-objects. The crossno
member references the REFXOBJ structure corresponding to the object in the TRKBLOCK structure of the appropriate track block. The ptRef
member contains the coordinates of the object‚s reference point. The nVertices
value is the number of vertices contained in the extra-object ; the coordinates of these vertices are stored in the vert
member. All coordinates are relative to the reference point. The shadingVertices
member describes the shading applied to each vertex : the least significant byte is the blue component, the next byte is the green component, the next byte is the red component, and finally the most significant byte is the alpha value. The nPolygon
value is the number of polygons making up the extra-object, and the polygons themselves are stored in the polyData
member.
For animated extra-objects, the XOBJDATA structure becomes :
typedef struct XOBJDATA {
long type;
long crossno;
long unknown;
short unknown2[9];
char type3,objno;
short nAnimLength,unknown3;
struct ANIMDATA animData[nAnimLength];
long nVertices;
struct FLOATPT vert[nVertices];
long shadingVertices[nVertices];
long nPolygons;
struct POLYGONDATA polyData[nPolygons];
} XOBJDATA;
The type
member equals 3 for animated extra-objects. The type3
member also equals 3, while the objno
member contains the object‚s sequence number inside the track block. The nAnimLength
member stores the length of the animation sequence ; the successive object positions are stored in the ANIMDATA substructures contained in the animData
member.
The nVertices
value is the number of vertices in the object ; the coordinates of these vertices are stored in the vert
member. All coordinates are relative to the reference points specified in the animation data. The nPolygon
value is the number of polygons making up the object, and the polygons themselves are stored in the polyData
member.
3. ANIMDATA structure (size : 20 bytes)
typedef struct ANIMDATA {
struct INTPT pt;
float costheta,sintheta;
} ANIMDATA;
This structure describes the position of an animated extra-object. The pt
member provides the position of the object‚s reference point (it is similar to the ptRef
member of a static extra-object XOBJDATA structure, but in the case of an animated object the reference point moves during animation). The costheta
and sintheta
members describe the rotation to be applied to the object (this rotation always occurs in the XY plane, the Z coordinate is not affected).
1. FRD file structure
typedef struct FRDFILE {
char header[28];
long nBlocks;
struct TRKBLOCK trk[nBlocks+1];
struct POLYGONBLOCK poly[nBlocks+1];
struct XOBJBLOCK xobj[4*(nBlocks+1)+1];
long nTextures;
struct TEXTUREBLOCK texture[nTextures];
} FRDFILE;
FRD files start with a 28-byte header, followed by the number of the last track block. The actual number of blocks is nBlocks+1
rather than nBlocks
. The first TRKBLOCK structure is at offset 32 in the file ; the TRKBLOCK structures are followed by the POLYGONBLOCK structures (one per track block), and the XOBJBLOCK structures (four per track block, plus one for global objects). These data are followed by the number nTextures
of track textures, and by TEXTUREBLOCK structures which describe how these textures are obtained from the bitmaps stored in the QFS files.
2. TEXTUREBLOCK structure (size : 47 bytes)
typedef struct TEXTUREBLOCK {
short width,height;
long unknown;
float corners[4][2];
long unknown2;
char islane;
short texture;
} TEXTUREBLOCK;
This structure stores the properties of a track texture ; the polygon textures described in POLYGONDATA structures always refer to one of these structures rather than directly to a QFS bitmap. The width
and height
members indicate the size of the bitmap in pixels. The corners
member is a series of four planar coordinates, used to describe the positions of the four texture corners with respect to the QFS bitmap ; these coordinates are often larger than 1, which makes it possible to tile several copies of a QFS bitmap into a single track texture. The islane
member equals 1 if this texture is used to display road lanes (in which case it is not connected to a QFS file texture), and 0 in all other cases. Finally, the texture
member indicates the relevant entry in the QFS file (except in the case of road lane textures where it represents the number of the relevant predefined texture).
The textures used for road lanes (i.e. those for which islane
equals 1) correspond to 14 standard predefined textures rather than QFS textures : texture 0 is a yellow solid line, texture 1 is a yellow dotted line, texture 2 is a double yellow solid line, textures 3 and 4 are double yellow lines (one solid line with one dashed line). Textures 5 to 9 are identical to textures 0 to 4 but are white instead of yellow. Textures 10 and 11 are solid tire marks, 12 and 13 are lighter tire marks.
WARNING : the TEXTUREBLOCK structure causes byte-alignment problems with the default options of most compilers, as its members are not aligned on even addresses ; beware of this when writing track editing software.
1. COL file structure
typedef struct COLFILE {
char collID[4];
long version;
long fileLength;
long nBlocks;
long xbTable[nBlocks];
struct XBCOLBLOCK xb[nBlocks];
} COLFILE;
The COL file has a structure comparable to the COL files of NFS2, but nearly all XBCOLBLOCK structures have changed. The only really useful information in COL files is the virtual road information, which is not redundant with what can be found in FRD files ; the other structures are used to describe animated global objects, and their interest is no longer clear now that extra-objects can be animated too... however it turns out that some of the NFS3 tracks have their global objects described in the COL file rather than in the FRD file, so the description below is still necessary.
The collID
member contains the characters ‘COLL‚ ; the version
member equals 11. The fileLength
member contains the length of the COL file. The file contents are stored in XBCOLBLOCK structures, which serve various purposes depending on their XBID identifier. The offset of each XBCOLBLOCK structure in the COL file is stored in the xbTable
member ; these offsets are relative to the beginning of the XB table, so one must add 16 in order to get an absolute offset in the COL file.
The number of blocks is either 2 if there are no global objects in the COL file, or 4 or 5 if there are global objects. The first block is used for a texture table, providing the conversion between QFS bitmaps and animated COL object textures ; it is always present, even when there are no animated COL objects, and its contents are not related in any way with the texture table stored in the FRD file. The next blocks are only present if there are animated COL objects : first a block containing the 3D structures of the COL objects, followed by one or two blocks containing the objects themselves. The last block, which is always present, contains important virtual road information.
2. XBCOLBLOCK structure (variable size)
typedef struct XBCOLBLOCK {
long size;
short xbid;
short nrec;
struct (COLTEXTUREINFO|
COLSTRUCT3D|
COLOBJECT|
COLVROAD) data[nrec];
} XBCOLBLOCK;
The size
member contains the total length in bytes of the XBCOLBLOCK. The xbid
member is an identifier which describes the meaning of the block‚s contents ; the nrec
member contains the number of data records in the XBCOLBLOCK. The meaning of these data records depends on the XBID value.
The following XBCOLBLOCK types are legal in NFS3 COL files (in their order of apparition in the COL file) :
a) COL texture information (XBID 2) (8-byte data records)
typedef struct COLTEXTUREINFO {
short texture;
short unknown1;
short unknown2;
short unknown3;
} COLTEXTUREINFO;
These records store the description of a COL texture in terms of a QFS bitmap. The texture
member refers to an entry in the QFS file.
b) COL 3D-structure information (XBID 8) (variable size records)
Before describing a COL 3D-structure record, we start with two auxiliary structures (vertices and polygons) :
typedef struct COLVERTEX {
struct FLOATPT pt;
long unknown;
} COLVERTEX;
This structure describes a vertex of a COL 3D structure. The pt
member contains coordinates relative to the reference position stored in the COL object information block.
typedef struct COLPOLYGON {
short texture;
char vert[4];
} COLPOLYGON;
This structure describes a polygon of a COL 3D structure. The texture
member refers to an element in the COL texture information block, while the v
member contains the numbers of the four vertices making up the polygon.
typedef struct COLSTRUCT3D {
long size;
short nVert,nPoly;
struct COLVERTEX vertex[nVert];
struct COLPOLYGON polygon[nPoly];
} COLSTRUCT3D;
The size
member stores the size of the data record ; the nVert
and nPoly
members contain the numbers of vertices and polygons respectively. The vertices and polygons themselves are stored in the vertices
and polygon
members.
c) COL object information (XBID 7 or 18) (variable size records)
These data records contain the description of a COL object ; however the 3D structures (vertices and polygons) themselves are stored in the COL 3D structure block. There are two versions depending on whether the object is static or animated. For static objects the structure is the following :
typedef struct COLOBJECT {
short size;
char type;
char struct3D;
struct INTPT ptRef;
} COLOBJECT;
For static objects, the size
member is equal to 16 and the type
member is 1. The struct3D
member refers to the relevant data record in the previous XBCOLBLOCK (3D structure information), and the ptRef
member contains the object‚s reference position.
For animated objects, the structure becomes
typedef struct COLOBJECT {
short size;
char type;
char struct3D;
short animLength;
short unknown;
struct ANIMDATA animData[animLength];
} COLOBJECT;
For animated objects, the size
member contains the size of the record, and the type
member is 3. The struct3D
member refers to the relevant data record in the previous XBCOLBLOCK (3D structure information). The animLength
member contains the length of the object‚s animation, and the successive object positions are sored in the animData
member (the ANIMDATA structures are the same ones as in the description of animated FRD extra-objects).
d) COL virtual-road information (XBID 15) (36-byte data records)
The following structure is used to store a vector in a COL file :
typedef struct COLVECTOR {
signed char x,z,y,unused;
} COLVECTOR;
The three vector components are normalized so the vector has length 128.
typedef struct COLVROAD {
struct INTPT refPt;
long unknown;
struct COLVECTOR normal,forward,right;
long leftWall,rightWall;
} COLVROAD;
There is one COL virtual-road data record for each track node ; there are usually 8 track nodes (and therefore 8 COLVROAD records) for each FRD track block (see the nStartPos
and nPositions
members in TRKBLOCK structures).
The refPt
member stores the coordinates of the node‚s reference point (a point along the road). The normal
vector points upwards, the forward
vector points forward, the right
vector points to the right.
The leftWall
and rightWall
members indicate the distance between the node‚s reference point and the left and right track walls (this distance is counted along the direction pointed by the right
vector) ; as cars can‚t cross these track walls, it is necessary to ensure that all passable road polygons lie inbetween.