Skip to content

Latest commit

 

History

History
396 lines (306 loc) · 20.1 KB

File metadata and controls

396 lines (306 loc) · 20.1 KB

Miscellany

These are various odds and ends that do not fit into the earlier categories.

Version

Every level file begins with a uint32_t version number. This seems to be used by the engine to guarantee compatibility between various level editor versions and the game engine version. More generally, it can be used to determine what sort of level is being read.

Here are the known (observed) values for the version header:

  • 0x00000020 — Tomb Raider 1, Gold, Unfinished Business

  • 0x0000002D — Tomb Raider 2

  • 0xFF080038 — Tomb Raider 3

  • 0xFF180038 — Tomb Raider 3

  • 0x00345254 — Tomb Raider 4 and Tomb Raider 5

  • 0x63345254 — Tomb Raider 4 (demo versions)

Note
Early TR4 demos (e.g. September 15 demo) have whole level file packed into a single zlib chunk. Therefore, there is no header.
Note
TR5 version header is equal to TR4 version header. So there is no way to tell TR4 level from TR5 level judging only by this header — you need to check filename extension as well.
Note
As it was noted, retail version of TR4 expects to load sound samples compressed in MS-ADPCM format, while TRLE version of TR4 loads uncompressed samples only. There is no way to tell retail version from TRLE version, as their version numbers are equal.

Palette

This consists of 256 [tr_colour] structs, one for each palette entry. However, the individual colour values range from 0 to 63; they must be multiplied by 4 to get the correct values.

This used for all 8-bit colour, such as 8-bit textures.

Object Textures

Object texture (or texture details in TRLE terms) keeps detailed information about each texture independently used in game. While it’s not texture image itself (these are kept inside texture tiles), it’s rather a reference to particular texture tile zone kept with all other necessary information to display this texture.

Object Texture Vertex Structure

This sub-structure used by object textures specifies a vertex location in textile coordinates. The Xpixel and Ypixel are the actual coordinates of the vertex’s pixel. The Xcoordinate and Ycoordinate values depend on where the other vertices are in the object texture. And if the object texture is used to specify a triangle, then the fourth vertex’s values will all be zero.

struct tr_object_texture_vert	// 4 bytes
{
    uint8_t Xcoordinate; // 1 if Xpixel is the low value, 255 if Xpixel is the high value in the object texture
    uint8_t Xpixel;
    uint8_t Ycoordinate; // 1 if Ypixel is the low value, 255 if Ypixel is the high value in the object texture
    uint8_t Ypixel;
};

Object Texture Structure (TR1-3)

It’s object texture structure itself. These, thee contents of ObjectTextures[], are used for specifying texture mapping for the world geometry and for mesh objects.

struct tr_object_texture	// 20 bytes
{
    uint16_t Attribute;
    uint16_t TileAndFlag;
    tr_object_texture_vert Vertices[4]; // The four corners of the texture
};

TileAndFlag is a combined field:

  • Bits 0..14 specify texture tile to use.

  • Bit 15: if set, it indicates that texture is used on a triangle face.

Attribute specifies transparency mode (i.e. blending mode) used for face with this texture applied. There are several ones available:

  • 0 — Texture is all-opaque, and that transparency information is ignored.

  • 1 — Texture uses alpha testing, i.e. it may contain opaque and completely transparent regions. In 8-bit colour, index 0 is the transparent colour, while in 16-bit colour, the top bit (0x8000) is the alpha channel (1 = opaque, 0 = transparent). In 32-bit textures, transparency is specified by full magenta colour value (RGB = 255,0,255) — i.e. pixel has to be magenta to be transparent.

  • 2 — {TR3}{TR4}{TR5} Texture uses alpha blending with additive operation. No depth sorting is done on alpha-blended textures.

Note

While blending modes 0, 1 and 2 were the only ones directly available for implementation in original level and animation editors, and therefore, only ones which can be encountered in object textures, there are actually several internal blending modes, which were primarily used for different sprite and particle types. These will be listed below:

  • 3 — Not implemented properly in PC version, but on PlayStation this type produces alpha blending with inversion operation, thus converting all the bright zones to dark, and dark zones to bright. This blending mode was used for smooth textured shadows, footprints and black smoke sprites. There is a remnant of this blending mode in the form of entity type named smoke emitter black.

  • 4 — Alpha-tested face without Z testing, i.e. depth information is ignored. Used for GUI elements (such as fonts) and skyboxes.

  • 5 — Unused. Possibly was used in PlayStation versions.

  • 6 — Wireframe mode. Used for ``line particles'', such as gun sparks, water drops and laser beams. Possibly was also used for debug purposes.

  • 7 — {TR4}{TR5} Forced alpha value. It’s ordinary alpha-tested face, but alpha value for this face is overridden with global variable. Used to fade out'' specific meshes, like vanishing enemy bodies or Semerkhet ghost in Tomb of Semerkhet'' level.

Object Texture Structure (TR4-5)

The structure introduced many new fields in TR4, partly related to new bump mapping feature.

For bump mapping, TR4 used fairly simple approach, which was actually not a true bump mapping, but multitexturing with additive operation. Therefore, bump maps were not normal maps mainly used for bump-mapping nowadays, but simple monochrome heightmaps automatically generated by level editor. This is a test screenshot comparison demonstrating same scene with and without bump mapping:

bump no

bump yes

Bump mapping turned off

Bump mapping turned on

Assignation of bump maps happened inside level editor, where each texture piece could be marked as either level 1 or level 2 degree of bump map effect. When level was converted, all texture pieces with bumpmaps were placed into separate texture tiles after all other texture tiles, following by the same amount of texture tiles with auto-generated bump maps arranged in the same manner as original texture tiles. Number of bumped texture tiles was kept in separate variable as well (see TR4 Level Format section).

So, when engine rendered a face with texture marked as bumped, it rendered original texture at first, then it jumped to the texture tile plus number of bumped texture tiles, and rendered one more texture pass on this face using texture from resulting texture tile and the same UV coordinates.

struct tr4_object_texture	// 38 bytes
{
    uint16_t Attribute;
    uint16_t TileAndFlag;
    uint16_t NewFlags;

    tr_object_texture_vert Vertices[4]; // The four corners of the texture

    uint32_t OriginalU;
    uint32_t OriginalV;
    uint32_t Width;     // Actually width-1
    uint32_t Height;    // Actually height-1
};

NewFlags is a bit field with flags:

  • Bits 0..2 — Mapping correction. It seems that these bits change the way the texture is applied.

  • Bits 11..12 — Specifies bump mapping level (see above), so can be either 00 = 0 (no bump mapping), 01 = 1 (level 1) or 10 = 2 (level 2).

  • Bit 15 — If set, the texture is for a triangle/quad from a room geometry. If not set, the texture is for a static mesh or model.

Width and Height are helper values which specify width and height for a given object texture.

OriginalU and OriginalV are unused values, which seem to identify original UV coordinates of object texture in TRLE texture page listings. These coordinates are getting messed up when level is compiled, so one shouldn’t bother about parsing them correctly.

{TR5} There is also null uint16_t filler in the end of each [tr4_object_texture].

Animated Textures

Animated textures describe sets of object textures that are cycled through to produce texture animations; they are a set of int16_t’s with the following format (not a ``real'' C/C++ structure):

int16_t NumAnimatedTextures

virtual struct
{
    int16_t NumTextureIDs; // Actually, this is the number of texture ID's - 1.
    int16_t TextureIDs[NumTextureIDs + 1]; // offsets into ObjectTextures[], in animation order.
} AnimatedTextures[NumAnimatedTextures];

If a texture belongs to an animated-texture group, it will automatically be animated by the engine.

There are two types of animated textures — classic frames and UVRotate:

  • Classic frames: These are ordinary animated textures, and the only type displayed prior to TR4. It is simply a list of textures that are cycled through in an endless loop; they are normally used as geographic elements of the levels (e.g. water surface, bubbling lava, Atlantean flesh walls), but practically, Tomb Raider engines are capable of applying animated textures to mesh geometry (this feature is primarily used in custom levels). The speed (interval) of animation is hardcoded, and varies from version to version. While in TR1-2 textures were animated relatively slowly, in TR3 onwards they were sped up.

  • UV Rotate: Beginning from TR4, there is a new scheme for animated textures, called UVRotate. According to its name, it continuously shifts vertical texture coordinate while preserving texture size, which creates an effect of moving texture. For example, you can see it in action in TR4’s angkor1.tr4, room #76:

uvrotate

In foreground, you can see alpha-blended waterfall object animated with UVRotate. In background, UVRotate animation is also applied to room mesh.

UVRotate mode is engaged by specifying UVRotate command in level script entry, which takes rotation speed as an argument, where speed is amount of pixel shift per each frame, considering fixed 30 FPS framerate (speed value may be negative, and in such cases pixel shift occurs in reverse direction). If such command is found (and argument is not zero — for example, UVRotate = 4), engine uses special variable value kept in level file, NumUVRotates, to determine if animation range belongs to UVRotate mode or classic frames mode. Then, if it belongs to UVRotate mode, each frame of this range is treated as individual rotating texture.

Note

There is also special case when UVRotate texture mode is engaged. When a texture is applied to a model with specific ID (so-called waterfall objects), then it is also considered UVRotate animated texture, even if it doesn’t belong to animated texture range, but only if it is a texture applied to a first face in the first mesh of the model. If there are other textures applied to other faces of a waterfall object, they won’t be considered as UVRotate.

The speed of animation for waterfall objects is not affected by UVRotate script command. Instead, it is hardcoded value of 7.

Cameras and Sinks

This data block serves for two different purposes, albeit keeping the same structure for both. First purpose is to provide positions to switch the camera to using Camera trigger action, and the second purpose is to move Lara to specified position when she is underwater, and Underwater Current trigger action was used.

struct tr_camera // 16 bytes
{
    int32_t x;
    int32_t y;
    int32_t z;
    int16_t Room;
   uint16_t Flag;
};

X, Y and Z values are coordinates of a given camera or sink. When used with camera, it is an origin point of a camera. When used with sink, it is a point, towards which Lara is pushed.

Room value specifies the room where camera is placed. For sink cases, this value is used to define strength of the current which moves Lara underwater.

Flag value for cameras specifies whether it’s possible to breakout from fixed camera angle. If first bit of this field is set, camera becomes persistent, and it’s not possible to bypass it with look or draw weapon buttons. For sinks, Flag value contains Box index — which is used as pathfinding reference when Lara is pushed towards sink via underwater current trigger action.

Flyby Cameras

{TR4}{TR5} Flyby cameras are cinematic interludes, in which camera flies from one point to another using spline trajectory. Each point in such sequence is a single flyby camera, and current camera properties (position, direction, roll, FOV, speed, and some more) are calculated by interpolating corresponding values from such flyby camera points — for example, if camera 0 has speed value of 10, and camera 1 has speed value of 5, then speed will gradually change from 10 to 5 when moving from one to another.

struct tr4_flyby_camera  // 40 bytes
{
    int32_t x;		// Camera position
    int32_t y;
    int32_t z;
    int32_t dx;		// Camera angles (so called "look at" vector)
    int32_t dy;
    int32_t dz;

    uint8_t Sequence;
    uint8_t Index;

   uint16_t FOV;
    int16_t Roll;
   uint16_t Timer;
   uint16_t Speed;
   uint16_t Flags;

   uint32_t Room_ID;
};

Sequence is a number of flyby camera ``chain'' this particular camera belongs to. Maximum amount of flyby sequences in single level is 8 (however, this limit was raised to 64 in TREP).

Index specifies order of the cameras in this particular sequence. Camera with index 0 will be first one in sequence, index 1 means camera will be second in sequence, and so on.

Room_ID should be valid for a given flyby camera, so it will display properly, as well as have the ability to activate heavy triggers.

FOV changes this particular camera’s field of view. The value is 182 times higher than the value entered in the TRLE.

Roll changes roll factor of a particular camera. When this parameter is not zero, camera will rotate either left or right along roll axis, creating so-called ``dutch angle''. The value is 182 times higher than the value entered in the TRLE.

Timer field mainly used to stop camera movement for a given time (in game frames). As this parameter is temporal, it won’t be interpolated between two cameras.

Speed specifies movement speed for this particular camera. The value is 655 times higher than the value entered in the TRLE.

Flags is an array of bit flags specifying different camera options:

  • Bit 0 — Make a cut to flyby from Lara camera position. Without it, it’ll pan smoothly.

  • Bit 1 — {TR4} Tracks specified entity position (from Entities[] array). {TR5} Creates a vignette around the picture, giving impression of "subjective" camera.

  • Bit 2 — Infinitely loop sequence.

  • Bit 3 — Used only with first camera in a sequence: whole sequence is treated merely as a camera rails'', and camera itself focuses on Lara, thus creating tracking'' camera. Best example is `tracking'' view in `ALEXHUB2.TR4, rooms #23 and #31.

  • Bit 4 — {TR4} Camera focuses on Lara’s last head position. {TR5} For TR5, this flag is now used to hide Lara for this camera.

  • Bit 5 — Camera continuously focuses on Lara’s head, overriding own angle.

  • Bit 6 — Used only with last camera in a sequence: camera smoothly pans back to Lara camera position.

  • Bit 7 — When flyby arrives to this position, cuts to specific camera in same sequence. Next camera number is specified in Timer field of this camera.

  • Bit 8 — Stops camera movement for a given time (see Timer field).

  • Bit 9 — Disables look keypress breakout.

  • Bit 10 — Disables all Lara controls for all next camera points. Also engages widescreen bars to create cinematic feel.

  • Bit 11 — Overrides Bit 10 controls lock, enabling them back. Widescreen bars remain unaffected.

  • Bit 12 — {TR5} Make screen fade-in.

  • Bit 13 — {TR5} Make screen fade-out.

  • Bit 14 — Camera can activate heavy triggers, just like particular kinds of entities (boulders, pushables, etc.). When camera is moving right above heavy trigger sector, it will be activated.

  • Bit 15 — {TR5} TRLE for TR5 says this flag is used to make camera one-shot, but it’s not true. Actual one-shot flag is placed in extra uint16_t field at 0x0100 for flyby camera TrigAction.

Cinematic Frames

These are camera positionings and properties for cutscene frames. All the entity animations are specified separately, and they are not synced with actual camera positions.

Note

{TR1} For each cutscene, game applies certain hardcoded set of parameters:

switch (CutLevelID) {
    case 0:	// CUT1.PHD
        levelPosX = 36668;	// X pivot position
        levelPosZ = 63180;  // Z pivot position
        levelRotY = -23312; // Y rotation
        trackID = 23;		// Audio track index
        break;
    case 1: // CUT2.PHD
        levelPosX = 51962;
        levelPosZ = 53760;
        levelRotY = 16380;
        trackID = 25;
        break;
    case 2: // CUT3.PHD
        levelRotY = 0x4000; // Default rotation, 90 degrees
        FlipMap();			// Do a flipmap
        trackID = 24;
        break;
    case 3:	// CUT4.PHD
        levelRotY = 0x4000;
        trackID = 22;
        break;
}

levelPosX, levelPosZ and levelRotY parameters are used, if defined, to set-up initial camera pivot position and Y axis rotation. If some of these parameters are not defined (which is the case for CUT3.PHD and CUT4.PHD), they are borrowed from X and Z position and Y rotation of entity with ID #77 (slot used for main cutscene actor, usually Lara). Also, same parameters are used as master model matrix (i. e. multiplied by them instead of using their own position and rotation) for models with IDs #77-79 (which are slots for cutscene actors).

struct tr_cinematic_frame // 16 bytes
{
    int16_t targetX; // Camera look at position about X axis,
    int16_t targetY; // Camera look at position about Y axis
    int16_t target2; // Camera look at position about Z axis
    int16_t posZ;    // Camera position about Z axis
    int16_t posY;    // Camera position relative to something (see posZ)
    int16_t posX;    // Camera position relative to something (see posZ)
    int16_t fov;
    int16_t roll;    // Rotation about X axis
};

All target and pos parameters, as well as roll parameter, are encoded in the same manner as Angle parameter for tr_entity structure.

LightMap

A 32*256 array of uint8_t which is apparently for applying light to 8-bit colour, in some documentation called ColourMap. The current palette index and lighting value are used to calcuate an index to this table, which is a table of palette indices.

The Tomb Raider series' software rendering, like that of most real-time-3D games, uses 8-bit colour for speed and low bulk; however, there is the serious problem of how to do lighting with 8-bit colour, because doing it directly is computationally expensive. The usual solution is to arrange the palettes' colours in ramps, which the engine then follows in the appropriate directions. However, the TR series' palettes generally lack such neat ramps.

But the TR series has a more general solution, one that does not require palettes to have colour ramps. It uses precalculated lighting tables, the ColourMap objects. These contain translations of a colour value and a lighting value, listed by palette index. The translation goes as follows:

n = ColourMap[256 * k + i];

where i is the original palette index, k is determined from the lighting value, and n is the new palette index. The lighting index k varies from 0 to 31, and the corresponding lighting value is, for TR1,

2 - k / 16

and for TR2 and TR3,

2 - (k + 1) / 16

This may be associated with the curious fact of the lighting values in the data files increasing in the ``wrong'' direction in TR1 and TR2, with 0 being full brightness and greater values being darker.