About this document

This document describes the file formats as used by Ultimate Stunts. The information described here applies to the most recent version, which is 0.7.5 at the time of writing, but many things also apply to older and newer versions. This document does NOT describe the configuration files (*.conf); for these files, please have a look at the documentation of the corresponding programs or at the comments in the files themselves.

This file contains the following sections:

General information about tracks

An UltimateStunts track is similar to an old stunts track in one important aspect: it consists of tiles. Each tile contains a 3D model of a piece of the track, e.g. a road, a piece of water, a bridge, or a tunnel. Tile-information is stored in files, each file containing just one tile.

The track-file, describing the track, contains references to the tile-files. Each tile can be used several times in one track, and in different orientations. So, if your track contains several straight roads in different directions, you'll only need one straight-road tile.

All tiles have the same sizes. Structures larger than the standard tile size, like wide corners, are built from smaller tiles. Tiles in a track are ordered in horizontal grids. Every point in the grid contains an id referring to the tile, an integer describing the orientation of the tile, and an integer describing the vertical position of the tile. Because of that last integer, it is possible to make tracks with very large height differences.

In one track, more than one of these grids can be used. This way, a threedimensional grid is created effectively, and tiles can be placed above each other. This results in the possibility of underground tunnels, viaducts crossing each other, etc. etc.

The track-file (*.track) format

The track file is an implementation of the information described above. Track files are text files, so you should be able to edit them with a text editor. A track file has the following sections (in this order): Except for the header, each section begins with a line containing "BEGIN", and ends with a line "END". Inside a section, no comments should be placed. Outside a section, all data is ignored, but to make clear that it is a comment, each comment line should begin with a "#".

The header

The header, consisting of four lines, has the following syntax:
TRACKFILE
length
width
height
The word "TRACKFILE" is a magic word to make sure we are dealing with a track file. The length and width parameters give the sizes of the grids, and height is the number of grids used. Every number in the header must be consistent with the rest of the file, or else errors will occur while loading the track.

The texture section

The texture section can be used to pre-load certain textures. Starting from version 0.7.5, it is no longer necessary to load all textures in this way, so usually it will be empty. However, even when it is empty, its "BEGIN" and "END" tags should still be present.

Each line in the texture section contains a filename of a texture image. These filenames are interpreted as being relative to the datafiles directory. The most common file formats (JPEG, PNG etc.) are supported through the SDL_image library. Texture images must have a width and height that are powers of 2 (8, 16, 32, 128 etc.).
After the filename, some optional parameters can be given, in the form of name=value pairs, separated by spaces. The following parameters can be set:

If the scale parameter (which must be a power of 2) is larger than 1, a smaller texture resolution is used. The resolution is calculated the following way:
resolution_used = texture_maxsize / (scalehint * lod)
The lod (Level Of Detail) used depends on the distance between tile and camera; a texture is loaded with lod = 1, 2, 4 and 8.
If the size of the texture in the file is smaller than resolution_used, then the file resolution is used. If resolution_used is smaller than 8, then no texture is used. Instead, textureless surfaces with the average color of the texture are used.

The background section

This section contains a description of the environment. This section should contain a number of "name = value" pairs, with the following meanings:
namemeaningformat
skyThe sky/clouds texture filenamestring
horizonThe horizon texture filenamestring
envmapThe reflection map filenamestring
lightdirThe direction where the light goes tothree coordinates separated by commas, vector length should be 1
lightcolThe light color ("diffuse light")red, green, blue components between 0.0 and 1.0
ambientcolThe shadow color ("ambient light")red, green, blue components between 0.0 and 1.0
skycolThe sky color at the Zenithred, green, blue components between 0.0 and 1.0
horizonskycolThe sky color at the Horizonred, green, blue components between 0.0 and 1.0
fogcolThe fog colorred, green, blue components between 0.0 and 1.0
envcolThe modulation color of clouds and horizon texturered, green, blue components between 0.0 and 1.0
See the image below:

The tile section

Each line in the tile section has the following syntax:
filename[:flags] [texture_id_0 [... [texture_id_n]]]
The filename, relative to the datafile dir, refers to the 3D-model to be used. This can either be a .glb file, or (version 0.7.5 or later) a tile's .conf file. In the case of a .conf file, the flags and texture_id values are not necessary, and should not be used. It is recommended to use .conf files whenever possible in this section. When using .glb files, you may need to specify the flags and texture_id values:

The flags give some additional information to Ultimate Stunts. Currently, these flags are recognised:

The texture_id parameters are indices to the textures section. (The first texture has id=0, the second id=1 etc.) These parameters describe which textures are used by this tile. This way, it is even possible to use the same tile with different textures:
BEGIN
...
the_same_filename.glb 1 2 3
the_same_filename.glb 4 5 6
...
END
The order in which the indices are given is important: e.g. for a road tile the first index could refer to a road texture, and the second to a grass texture. When swapping them, you get effects you don't want. :-)

The grid sections

Every grid section contains the data of one grid. The first grid section in the file contains layer 0, which should contain the lowest tiles of the track. Every higher layer should contain the tiles placed above the tiles of the lower layer.

Every line in a grid section contains the data of one line of tiles. The number of lines in a section should equal length in the header. The number of items in a line should equal width.
Every line begins with a tab, and items are separated with a tab. (DON'T use spaces here!). Every item has the following syntax:

tile_id/rotation/height
tile_id is a reference to an item in the tiles section. rotation is an integer describing the orientation of the tile, via
degrees_clockwise = 90 * rotation
height is an integer describing the vertical position, via
y_offset = vertical_tilesize * height

The checkpoint section

This section defines the routes that can be followed by players in order to finish the track. When a player does not follow one of the alternatives, he/she gets penalty time.

Every checkpoint line in this section contains the tile coordinates of the checkpoint, followed by a ":" and a time value. The tile coordinates are integer numbers, that define a position in the grid: first the west-east position, then the north-south position and then the layer number (e.g. 0 for the lowest tile at that position). The time value is the time after starting when a fast car could be in the tile. The penalty time is calculated by taking the difference between the time of the last valid position and the new position, and multiplying this difference to make it bigger.

When a track has only one route, then the first checkpoint should be the start tile and the second checkpoint should be the tile directly after the start. After that, not every tile needs to be a checkpoint, but it is recommended to put checkpoints at strategic points like corners and the top of a looping. The last checkpoint should be the start/finish tile (when the start is also the finish), or, when the finish is at a diferent place, the last two checkpoints should be the finish and the tile directly behind the finish.

When there are multiple routes, the first route should be the fastest one. Then, the fastest alternative route should be defined, then all other alternatives, sorted on driving time, and as last the slowest alternative. The fastest route should be given in exactly the same way as described for single-route tracks. Alternative routes are slightly different from the first route:

The first checkpoint of an alternative route is not the start, but the last checkpoint that is shared with previously defined routes (Normally this is the tile where the two separate). The time of this tile is set to 0.0 seconds, and the time values of other checkpoints of this alternative should be given relative to this first point. The last checkpoint of the alternative should be the first checkpoint that is shared with previously defined routes. When Ultimate Stunts finds that there already is a checkpoint at that position, it knows that this alternative ends, and that following positions define new alternative routes.

Examples:


-finish-<-B-<--\
          |    |
          ^    ^
          |    |
          A->--/
          |
          ^
          |
        start

gives:

BEGIN
{start pos} : 0.0
{checkpoints between start and A}
{A pos} : {time of A}
{checkpoints short path between A and B}
{B pos} : {short time of B}
{checkpoints between B and finish}
{finish pos} : {short finish time}
{pos after finish} : {time}
{A pos} : 0.0
{checkpoints long path between A and B}
{B pos} : {long time of B - time of A}
END

The 3D-editor texture file (textures.dat)

In order to use the right texture when editing a 3D-model file, the 3D editor needs a file which describes the textures to be loaded. This file is called textures.dat, and it should be located in the current directory.

The header section of this file only contains the line "TEDITFILE". The only other section in this file (beginning with the line "BEGIN" and ending with "END") contains the following lines:

The old 3D-model (*.gl / *.glt) format

In previous version (0.5.2 and before), all 3D models in UltimateStunts were stored in a text format. OpenGL programmers will recognise its syntax, because it's very similar to the commands in the openGL library. This section describes this old *.gl format. The new *.glt format is an extension to the *.gl format, and it allows vertex array models. It will be described here in the future. An important thing to know is that *.glt files are only used for editing: they cannot be loaded by Ultimate Stunts. Instead of that, version 0.5.3 and later use *.glb files.

Commands in *.gl files should be placed at the beginning of lines, so it's only possible to place one command per line, and no white space is allowed before the command.

Primitives

3D models in *.gl files are described with the same 3D primitives that are supported by openGL. Every primitive describes a certain kind of 3D polygon, with the verticed defined counterclockwise as seen from the side where the polygon should be visible. As an additional requirement, the order in which the primitives are defined in the *.gl file should be a "back to front" order as much as possible. This is necessary for users who disable Z-buffering.

The following 3D primitives are supported by the format: Quads, Triangles, Trianglestrip, Quadstrip and Polygon. A primitive is started with a line containing its name, and it is ended with "End". The following code describes two triangles:

Triangles
[code describing the first triangle]
[code describing the second triangle]
End

Vertex x,y,z

The most important command for describing primitives is the Vertex command. It describes one threedimensional point in the primitive, and should only be used within primitive code. The following example describes one square:
Quads
Vertex -5,0,-5
Vertex -5,0,5
Vertex 5,0,5
Vertex 5,0,-5
End
In the same way, triangles, triangle strips, quad strips and polygons can be defined. The coordinates in Ultimate Stunts should be given in meters. For a tile, this means a range between -20 and 20 for x and z, and a range between 0 and 12 for y.

Color red,green,blue

The color command is used to change the drawing color. This can be done both before and within a primitive definition (like in openGL). The parameters red,green and blue should be within the range [0,1]. Usage of Color is described by the following examples:

A red triangle:

Color 1,0,0
Triangles
Vertex -1,0,0
Vertex 0,1,0
Vertex 1,0,0
End
A square with a red/yellow color gradient:
Color 1,0,0
Quads
Vertex -5,0,-5
Vertex -5,0,5

Color 1,1,0
Vertex 5,0,5
Vertex 5,0,-5
End
A polygon with lots of colors:
Polygon
Color 1,0,0
Vertex -3,0,-5
Color 0.5,0.5,0
Vertex -5,0,-3
Color 0,1,0
Vertex -5,0, 3
Color 0,0.5,0.5
Vertex -3,0, 5

Color 0,0,1
Vertex  3,0, 5
Color 0.5,0,0.5
Vertex  5,0, 3
Color 0,0,0
Vertex  5,0,-3
Vertex  3,0,-5
End

Opacity level

Defines how opaque the next primitives will be. 1.0 is fully opaque, 0.0 is fully transparent.

Reflectance level

Defines How well the reflection of the next primitives will be. 1.0 is fully reflecting, 0.0 is non-reflecting.

Normal x,y,z

The normal vector is used to calculate shadows. Its length should be 1, and it should be perpendicular to the surface. Normal can be used both before and within a primitive definition. The following example describes a quad with a correct normal vector:
Normal 0,0.96,0.29
Quads
Vertex 5,0,5
Vertex 5,3,-5
Vertex -5,3,-5
Vertex -5,0,5
End

Texture texture_id

This enables the usage of a texture. texture_id refers to one of the indices in the track file, called texture_id_n in the description above. The texture command can only be used outside primitive-definitions. The following listing, describing how to use the texture "texture_file.rgb", will make things clearer:
----------tracks/default.track-----------
TRACKFILE
...
#Texture section
BEGIN
texture_one.rgb
texture_two.rgb
texture_file.rgb
...
END
...
#Tiles section
BEGIN
...
example_tile.gl 0 2
...
END
...
-----------------------------------------
------------example_tile.gl--------------
...
Texture 1
...
-----------------------------------------
Here, texture_id = 1, and refers to texture_id_1. texture_id_1, being 2, refers to the third texture, which is "texture_file.rgb".

Note that directly using .gl files in a track is no longer possible, so this example is no longer completely correct. Instead, convert your .gl(t) file to a .glb file with the ustunts3dedit program, and use that .glb file in your track.

Notex

Notex disables the usage of textures.

TexCoord u,v

Attaches texture coordinates to vertices, used in a similar way as Normal and Color. If u and/or v are given a range greater than [0,1], the texture is tiled. The following example, showing a square with texture 0 attached to is, should be clear now:
Texture 0
Normal 0,1,0
Quads
TexCoord 1,1
Vertex 5,0,5
TexCoord 1,0
Vertex 5,0,-5
TexCoord 0,0
Vertex -5,0,-5
TexCoord 0,1
Vertex -5,0,5
End
Notex

Lod levels

Defines in which LOD (Level Of Detail) levels the following objects should be visible. levels is a combination of the characters 1, 2, 3, 4 and c. 1 is the "high detail", "close-up" LOD, 4 is the "low detail", "far away" LOD, and c is the collision detection LOD. For example:
Object A
Lod 1c
Object B
Lod 1234c
Object C
Here, in all LODs the objects A and C are visible, and object B is also visible in LOD 1 and it is used for collision detection. The default LOD is 1234c.

RotationAnimation origin;velocity

Enables the rotation animation for the most recently added primitive. This is not a state variable, so rotation animation is not enabled automatically for following primitives. Origin and velocity are settings for the rotation animation.

The new 3D model (*.glb) format

This fileformat has replaced the *.gl file format since version 0.5.3. It is a 3D geometry file format, designed to have the following advantages over the *.gl format: The general idea is that a model consists of objects (like the primitives in the *.gl format); objects have a name, a material definition, a set of vertices and an index array that defines the triangles. We will probably also want to add other kinds of data to the file, so there will be a possibility to add new types of objects to the file format.

Byte order

The byte order is defined in the file shared/binbuffer.cpp, as binbuffer objects are used to load and store data from *.glb files.

Top level format

magic number (4 byte)
objects
The magic number is there to make sure that it really is an Ultimate Stunts geometry file. It must be "\0GLB". The order in which the objects are in the file does matter sometimes. For example, this order defines in which order the objects are displayed, so if the z-buffer is disabled, then a bad order can look bad.

Object format

object type (4 byte)
object name size (4 byte)
object name (null-terminated, aligned to 4 byte size)
object data size (4 byte)
object data
The data size is the number of bytes in the object data (that excludes the object type and object data size). Depending on the object type, the object data can have the formats that are described below. For every format, the object type ID is placed within brackets.

Geometry object data 0.5 (type 0x00000000)

material data size (4 byte)
number of vertices (4 byte)
number of indices (4 byte)
material data
vertex array
index array
This object type is used in Ultimate Stunts prior to version 0.7.0. It is still supported, but its use is discouraged. Please use the Geometry object data 0.7 instead.

Material data

material modulation color r,g,b,a (4 byte)
material replacement color r,g,b,a (4 byte)
LODs (1 byte)
material reflectance (1 byte)
material emissivity (1 byte)
static friction coefficient (1 byte, range 0..16)
dynamic friction coefficient (1 byte, range ???)
unused (3 byte)
texture name (null-terminated, aligned to 4 byte size)
LODs contains a set of flags that describe in which LODs the object should be visible. The texture name can be a filename (relative to the Ultimate Stunts datadir), or a number. The modulation color is used to modulate the texture, the replacement color is used if the texture is disabled.

If the texture name is a number, then it can be set in the track file as a parameter. Colors can also be set as a parameter: if a==0 and b==255, then r is the number of the parameter. (this does not reduce the number of colors that can be set: if a==0 then the values of r,g,b don't matter.)

vertex element data

x,y,z (3*4 byte, signed int, unit = millimeter)
nx,ny,nz (3*4 byte, signed int, range -1..1)
tx, ty (2*4 byte, signed int, range -128..128)

index element data

i (4 byte)
The index refers to an element in the vertex array of the object.

Geometry object data 0.7 (type 0x00000001)

animation data size (4 byte)
material data size (4 byte)
number of vertices (4 byte)
number of indices (4 byte)
animation data
material data
vertex array
index array
This is the replacement of the Geometry object data 0.5 type. Animation data is added, and animation and material data is made extensible, so that later versions won't need a new object type too soon. Also, friction coefficients have been removed from the material data.

Animation data

Animation flags (4 byte)
Rotation origin x,y,z (3*4 byte, signed int, unit = millimeter)
Rotation velocity x,y,z (3*4 byte, signed int, range -1000..1000)
future additions (aligned to 4 byte size)
The animation flags contain a number of flags to describe which animation types are enabled for this object. The rest of the animation data contains parameters for these animation types. The flags variable is an OR'ed combination of values that correspond to animation types in the following way:

The rotation animation has the rotation origin and velocity as parameters. The object is rotated around the axis that goes through the rotation origin point, and has the direction of the rotation velocity. The rotation speed (in rad/second) is equal to the size of the velocity vector.

Material data

material modulation color r,g,b,a (4 byte)
material replacement color r,g,b,a (4 byte)
LODs (1 byte)
material reflectance (1 byte)
material emissivity (1 byte)
unused (1 byte)
texture name (null-terminated, aligned to 4 byte size)
future additions (aligned to 4 byte size)
LODs contains a set of flags that describe in which LODs the object should be visible. The texture name can be a filename (relative to the Ultimate Stunts datadir), or a number. The modulation color is used to modulate the texture, the replacement color is used if the texture is disabled.

If the texture name is a number, then it can be set in the track file as a parameter. Colors can also be set as a parameter: if a==0 and b==255, then r is the number of the parameter. (this does not reduce the number of colors that can be set: if a==0 then the values of r,g,b don't matter.)

vertex element data

x,y,z (3*4 byte, signed int, unit = millimeter)
nx,ny,nz (3*4 byte, signed int, range -1..1)
tx, ty (2*4 byte, signed int, range -128..128)

index element data

i (4 byte)
The index refers to an element in the vertex array of the object.

Replay (*.repl) format

A replay file contains a sort of movie of what happened during a race. For every moment in the race, car positions and orientations, and other state data are saved. The *.repl format is a mix of binary and UNIX-style text elements. So, whenever a "text line" is present, it is a '\n'-terminated string. The binary parts all use a format that is also used in network communication of Ultimate Stunts multiplayer games. They all use the functions in shared/binbuffer.cpp for writing high-level objects like strings or vectors.

header

The start of the header has the following format:
Magic header text (text line)
Track filename (text line)
Number of objects (text line containing decimal number)
The first line in a .repl file contains a magic header text. This text is there to see whether we are dealing with a real .repl file, and whether its format version corresponds to the replay loader version. At the time of writing (Ultimate Stunts 0.7.4), the header text is:
Ultimate Stunts replay format 0.7.4; network version ULTIMATESTUNTS 0.7.4
The next line contains the filename of the track file. So, the track itself is not included in the replay file, only its filename. After the track filename, the number of objects (e.g. cars) is written to the file.

Objects

Then, for every object, the following data is written:
Number of bytes in description (text line containing decimal number)
Object description (binary buffer)
The exact format of the object description is defined in simulation/objectchoice.cpp. Currently, this has the following format:
object type (8-bit unsigned integer, value = 5 for cars)
object's .conf filename (string)
player's name (string)

Frame data

Finally, for every frame in the "animation", the object state data is written in the file. A state data frame has the following format:
length of first object's state data (16-bit unsigned integer)
first object's state data
[length of second object's state data (16-bit unsigned integer)
second object's state data]
etc...
The length of an object's state data is re-written in every frame, because it can be different for every frame. The state data of a single object is a binary buffer, defined by its derived implementations of the getData and setData methods. Usually (like in cars), the base implementation of CMovingObject is used as a starting point. Currently the format is as follows:

The first part is the same for all kinds of moving objects (cars and other kinds of vehicles):

Object ID (8-bit unsigned integer)
Frame time (32-bit float, resolution 0.005)
Position (vector of 32-bit floats, resolution 0.001)
Orientation (vector of 16-bit floats, resolution 0.0002)
Velocity (vector of 16-bit floats, resolution 0.01)
Angular velocity (vector of 16-bit floats, resolution 0.001)
Next is a type-specific part. For cars (currently the only kind of moving objects), this currently is:
Steering angle (8-bit float, resolution 0.008)

Gear number (8-bit unsigned integer)
Drive axis angular velocity (16-bit float, resolution 0.4)
Engine gas fraction (8-bit float, resolution 0.008)

Wheel 0 angular velocity (16-bit float, resolution 0.4)
Wheel 0 angle (8-bit float, resolution 0.025)
Wheel 0 steering angle (8-bit, resolution 0.008)
Wheel 0 skid volume (8-bit, resolution 0.008)

Wheel 1 angular velocity (16-bit float, resolution 0.4)
Wheel 1 angle (8-bit float, resolution 0.025)
..etc.

flags (8-bit unsigned integer, used as bitmask)

[
collision radial velocity (8-bit float, resolution 0.40)
collision tangential velocity (8-bit float, resolution 0.40)
collision is fatal (8-bit unsigned integer, used as boolean)
]
The collision part is not always present: the 0x1 position in the flags value is set when collision data is present. Bit position 0x2 in flags is set when the car has crashed.

Future developments

New file formats

When new functionality is added, new file formats will be needed. The collision detection information will be fetched from the *.glb files, but it will also need, for example, material information, like friction coefficients. Of course, that nice Stunts sinking effect won't be possible if we don't know which surfaces represent water.

Changes in current file formats

The boundaries that prevent the cars from leaving the track should be defined in the track file.