A Descent level is encoded in a file called an RDL (Registered Descent
Level) file. This document describes in some detail the format of an
RDL file, and what each section and structure in the file is.
This document was written by Jon Hylands.
eMail author: Jon Hylands
RL2 specs - Descent 2 Level format
An RDL file is composed of three main parts:
- Header information
- Mine structures
- Objects
We will now examine each of these three parts in detail.
char[4] signature;
long version;
long mineDataOffset;
long objectsOffset;
long fileSize;
|
Every Descent RDL file starts with a four-bye signature string, which
is the ascii string "LVLP". The following four bytes is the version of
the RDL file, which, for Descent 1 is 1.
Following are two offsets into the RDL file, the first being the
offset of the mine structures, and the second being the offset of the
objects. Each is a four-byte long integer. If the RDL is part of a HOG
file, the offsets are relative to the position of the RDL file within
the HOG file.
short vertexCount;
short cubeCount;
Vertex verticies[vertexCount];
Cube cubes[cubeCount];
|
The mine structures are the verticies and cubes that make up the
structural portion of a level. It starts with two integers, one the
number of verticies, and one the number of cubes in the level.
Following the two counts is the vertex data. A vertex is defined as:
Thus, each vertex takes 12 bytes. Each coordinate of a vertex is a
32-bit fixed point number, in 16:16 format.
Following the vertex data are the cubes. A cube is a significantly
more complex structure, and is variable sized depending on a number of
factors.
Each cube starts with a 8-bit mask, that defines for each side whether
or not it has a neighbor (attached) cube, and also whether or not the
cube is an energy center.
cube neighbor bitmask:
bit 0: 1 if another cube attached to left side
bit 1: 1 if another cube attached to top (*)
bit 2: 1 if another cube attached to right (*)
bit 3: 1 if another cube attached to bottom (*)
bit 4: 1 if another cube attached to back (*)
bit 5: 1 if another cube attached to front
bit 6: 1 if the cube is an energy center
bit 7: <unused>
|
For bits 0-5 in the bitmask, if the bit is one, there is a
corresponding short (16 bit) value following the bitmask in the file
with the id of the cube that is attached on that side. For example, if
bit 5 = 1, the 2 bytes following the bitmask make up an integer which
is the id of the cube attached to this cube's left side. If the value
of the neighbor id is -2, that side of the cube is the special "end
wall", placed at the end of the exit tunnel.
Thus there can be anywhere from zero to six shorts following the
bitmask, describing the id of the cube's neighbors. Note that the
ordering is very important here - it goes (left, top, right, bottom,
back, front).
For example, let's say cube #15 has three neighbor cubes. On its left
is cube #17, on its right is cube #20, and below it is cube #25. The
children bitmask would look like this:
00001101
Following this would be three shorts:
17, 20, 25
The cube is defined in space by its eight verticies, which are in a
specific order. The verticies for each cube are specified by providing
an index into the above vertex structure for each one. Thus following
the neighbor information are eight shorts, specifying verticies 0
through 7 for the cube. The order of the verticies is defined by their
position in the cube, which is summarized in the following table:
- 0 - left, front, top
- 1 - left, front, bottom
- 2 - right, front, bottom
- 3 - right, front, top
- 4 - left, back, top
- 5 - left, back, bottom
- 6 - right, back, bottom
- 7 - right, back, top
Bit 6 in the bitmask is set to one if the cube is part of an energy
center. If this is the case, following the vertex values are three
values describing the energy center. They are defined as follows:
ubyte special;
byte energyCenterNumber;
short value;
|
Next comes the cube's static light value, which is stored as a short
fixed-point. A short fixed-point is a 16-bit number, in 4:12 format.
Next comes the walls bitmask. Bits 0-5 are once again set if the
corresponding side is a wall (which includes doors). For each bit
set, there is an unsigned byte value following with the id of the
wall. If the value is 255, it is considered to be -1, which means no
wall.
Finally, for each side of the cube that doesn't have a neighbor, or
does have a switch, there follows the texture information for that
side. This consists of the primary texture, optionally the secondary
texture, and the UVL's for the side.
The primary texture is a ushort, with the lower 15 bits being used as
the texture number. The high bit is set if this side has a secondary
texture associated with it. If it does have a secondary texture, the
secondary texture value (a 16-bit short) follows the primary texture
value.
The UVL structure defines, for each vertex on the side, the offset of
the texture, and the light value of that vertex on the side. UV is the
texture offset at that corner, and L is the light value for that
corner. The texture offset is a pair of short fixed point values,
which define how the texture is stretched.
Unfortunately Jon Hylands never finished this section of the document.
However they are quite similar to RL2's Object section:
RL2 specs - Descent 2 Level format