{{indexmenu_n>4}}
====== Meshes and Models ======
===== Overview =====
Nearly all of the non-geographic visual elements in TR (as well as a few parts of the landscape) consist of as meshes. A //mesh// is simply a list of vertices and how they’re arranged. The mesh structure includes a list of vertices as relative coordinates (which allows meshes to easily be placed anywhere in the world geometry), a list of normals (to indicate which side of each face is visible), and lists of Rectangles and Triangles, both textured and coloured. The elements of each [[trs:fundamentals#tr_face4|[tr_face4]]] or [[trs:fundamentals#tr_face3|[tr_face3]]] (or same version-specific) structure (Rectangles and Triangles) contain an offset into the ''%%Vertices[]%%'' array for the mesh. Other arrays (''%%Entities[]%%'', ''%%StaticMeshes[]%%'') do not reference the array ''%%Meshes[]%%'' directly, but instead reference the array ''%%MeshPointers[]%%'', which points to locations inside of ''%%Meshes[]%%'', inside of which the meshes are stored in packed fashion.
Pointer indexing system allows engine to share same mesh for numerous different models, and also easily implement a feature called //meshswap// — used when a puzzle is inserted into a hole, when Lara draws pistols, and so on.
While it may be not obvious, but every time you see mesh look is changed, it means that //meshswap// happened. There was never any other way to modify mesh looks in classic TRs.
===== Meshes =====
The sign of the number of normals specifies which sort of lighting to use. If the sign is positive, then external vertex lighting is used, with the lighting calculated from the room’s ambient and point-source lighting values. The latter appears to use a simple Lambert law for directionality: intensity is proportional to $\max(\langle \vec n, \vec o - \vec p \rangle, 0)$, with $\vec n$ being the normal, $\vec o$ the lit position, and $\vec p$ the light's position. If the sign is negative, then internal vertex lighting is used, using the data included with the mesh.
This is not a “real” C/C++ structure, in that the arrays are sized by the ''%%NumXXX%%'' elements that precede them.
{{anchor:tr_mesh}}
virtual struct tr_mesh // (variable length)
{
tr_vertex Centre;
int16_t CollRadius;
uint16_t Flags; // 0x01 for Flat Shaded, i.e. Normals should not affect lighting
int16_t NumVertices; // Number of vertices in this mesh
tr_vertex Vertices[NumVertices]; // List of vertices (relative coordinates)
int16_t NumNormals;
if(NumNormals > 0)
tr_vertex Normals[NumNormals];
else
int16_t Lights[abs(NumNormals)];
int16_t NumTexturedRectangles; // number of textured rectangles in this mesh
tr_face4 TexturedRectangles[NumTexturedRectangles]; // list of textured rectangles
int16_t NumTexturedTriangles; // number of textured triangles in this mesh
tr_face3 TexturedTriangles[NumTexturedTriangles]; // list of textured triangles
int16_t NumColouredRectangles; // number of coloured rectangles in this mesh
tr_face4 ColouredRectangles[NumColouredRectangles]; // list of coloured rectangles
int16_t NumColouredTriangles; // number of coloured triangles in this mesh
tr_face3 ColouredTriangles[NumColouredTriangles]; // list of coloured triangles
};
''%%Centre%%'' is usually close to the mesh’s centroid, and is the center of a sphere used for certain kinds of collision testing.
''%%CollRadius%%'' is the radius of that aforementioned collisional sphere.
''%%NumNormals%%'': If positive, it is a number of normals in this mesh. If negative, it is a number of vertex lighting elements (''%%abs%%'' value).
Depending on a value of ''%%NumNormals%%'', next data block is interpreted either as ''%%Normals[]%%'' array (in [[trs:fundamentals#tr_vertex|[tr_vertex]]] format) or ''%%Lights%%'' array (just standard ''%%int16_t%%'' values).
''%%NumTexturedTriangles%%'' and ''%%NumTexturedRectangles%%'' are respectively the number of triangular and rectangular faces in this mesh. Corresponding ''%%TexturedTriangles%%'' and ''%%TexturedRectangles%%'' array contain textured triangles and rectangles themselves.
''%%NumColoredTriangles%%'' and ''%%NumColoredRectangles%%'' are respectively the number of triangular and rectangular faces in this mesh. Corresponding ''%%ColoredTriangles%%'' and ''%%ColoredRectangles%%'' array contain colored triangles and rectangles themselves.
As coloured faces feature was removed since TR4, [[meshes_models#tr_mesh|[tr_mesh]]] structure was changed, and contain no data for coloured faces anymore:
{{anchor:tr4_mesh}}
virtual struct tr4_mesh // (variable length)
{
tr_vertex Centre;
int32_t CollRadius;
int16_t NumVertices; // Number of vertices in this mesh
tr_vertex Vertices[NumVertices]; // List of vertices (relative coordinates)
int16_t NumNormals;
if(NumNormals > 0)
tr_vertex Normals[NumNormals];
else
int16_t Lights[abs(NumNormals)];
int16_t NumTexturedRectangles; // number of textured rectangles in this mesh
tr_face4 TexturedRectangles[NumTexturedRectangles]; // list of textured rectangles
int16_t NumTexturedTriangles; // number of textured triangles in this mesh
tr_face3 TexturedTriangles[NumTexturedTriangles]; // list of textured triangles
};
===== Static Meshes =====
As the name tells, static meshes are meshes that don’t move (e.g. skeletons lying on the floor, spiderwebs, trees, statues, etc.) Usually it implies that static mesh is completely non-interactive, i.e. all it does is sitting there in place serving as an ornament.
{{:icons:tr4.png?nolink|TR4 only}}{{:icons:tr5.png?nolink|TR5 only}} Since TR4, certain static meshes became //destroyable// (either by shooting or exploding them), and even gained ability to activate //heavy triggers//. Such static meshes are called //shatters//. Engine tells shatter statics from ordinary ones judging by their IDs, i.e. shatter static mesh must be in a specific slot. This behaviour is hardcoded.
StaticMeshes have two //bounding boxes//. First one serves as visibililty box, and other is the collisional box. The former is being used for visibility testing, and the latter is used for collision testing.
{{anchor:tr_staticmesh}}
struct tr_staticmesh // 32 bytes
{
uint32_t ID; // Static Mesh Identifier
uint16_t Mesh; // Mesh (offset into MeshPointers[])
tr_bounding_box VisibilityBox;
tr_bounding_box CollisionBox;
uint16_t Flags;
};
{{anchor:tr_bounding_box}}
struct tr_bounding_box // 12 bytes
{
int16_t MinX, MaxX, MinY, MaxY, MinZ, MaxZ;
};
''%%VisibilityBox%%'' and ''%%CollisionBox%%'' boundaries is always stay axis aligned even after applying tr_room_staticmesh::Rotation (always have 90 degrees step). Additionally, the test whether to rotate the box or not relies on the mesh’s rotation being an exact multiple of 0x4000 (aka 90 degrees). If this is not the case, the box is not rotated, which results in wrong collision checks.
{{:icons:tr1.png?nolink|TR1 only}}{{:icons:tr2.png?nolink|TR2 only}}{{:icons:tr3.png?nolink|TR3 only}} ''%%Flags%%'':
* **Bit 0** (''0x0001''): no collision
* **Bit 1** (''0x0002''): is visible
===== Models =====
This defines a list of contiguous meshes that comprise one object, which is called a //model//. This structure also points to the hierarchy and offsets of the meshes (''%%MeshTree%%''), and also to the animations used (''%%Animation%%''); these will be described in detail below. If the Animation index is -1, that means that there are no predefined animations, and entity’s movement is all generated by the engine; an example is Lara’s ponytail or rolling balls from TR4 and TR5.
Some entities are really stationary, such as locks and the skybox, and some are not rendered at all, such as “camera target” points to aim the camera at, flame emitters, AI objects and other service entities. Such invisible models are frequently called //nullmeshes//, because usually they have null mesh index specified for them, and never actually use it.
{{:icons:tr1.png?nolink|TR1 only}}{{:icons:tr2.png?nolink|TR2 only}} Sometimes, model may refer to sprite or sprite sequence to draw itself (for example, pick-up items and flame emitters). In this case, model is replaced with sprite in run-time. This behaviour is hardcoded for specific model IDs.
{{:icons:tr4.png?nolink|TR4 only}}{{:icons:tr5.png?nolink|TR5 only}} Sometimes, model may have //two different versions// defined in level files — one is normal, and another is low-detailed one, with the latter used when camera position gets too far from them. These are called //MIP models//, and mostly exist for NPCs (enemies). Usually, their type IDs are one off their normal counterparts (for example, skeleton type ID in TR4 is //35//, and its MIP variation is //36//).
{{anchor:tr_model}}
struct tr_model // 18 bytes
{
uint32_t ID; // Type Identifier (matched in Entities[])
uint16_t NumMeshes; // Number of meshes in this object
uint16_t StartingMesh; // Stating mesh (offset into MeshPointers[])
uint32_t MeshTree; // Offset into MeshTree[]
uint32_t FrameOffset; // Byte offset into Frames[] (divide by 2 for Frames[i])
uint16_t Animation; // Offset into Animations[]
};
{{:icons:tr5.png?nolink|TR5 only}} There is an extra ''%%uint16_t%%'' at the end of [[meshes_models#tr_model|[tr_model]]] structure, which is always ''%%0xFFEF%%'' and used for alignment. Consider it while parsing.
===== Entities =====
Entities are the actual instances of entity types, consisting either of //models// or //sprites// (with the latter existing in TR1-2 only). For an entity to appear in a level, it must be referenced in the ''%%Models[]%%'' array. Multiple instances of the same model are possible (e.g. two identical tigers in different rooms are represented using two entries in ''%%Entities[]%%'', one for each).
Entity structure has gone through different variations across game versions, so we’ll list them all.
==== TR1 Entity Structure ====
{{anchor:tr_entity}}
struct tr_entity // 22 bytes
{
int16_t TypeID; // Entity type ID (matched in Models[])
int16_t Room;
int32_t x; // Item position in world coordinates
int32_t y;
int32_t z;
int16_t Angle;
int16_t Intensity1;
uint16_t Flags;
};
==== TR2-3 Entity Structure ====
{{anchor:tr2_entity}}
struct tr2_entity // 24 bytes
{
int16_t TypeID;
int16_t Room;
int32_t x;
int32_t y;
int32_t z;
int16_t Angle;
int16_t Intensity1;
int16_t Intensity2; // Like Intensity1, and almost always with the same value.
uint16_t Flags;
};
==== TR4-5 Entity Structure ====
{{anchor:tr4_entity}}
struct tr4_entity // 24 bytes
{
int16_t TypeID;
int16_t Room;
int32_t x;
int32_t y;
int32_t z;
int16_t Angle;
int16_t Intensity1;
int16_t OCB; // Replaces Intensity2, see further for explanations.
uint16_t Flags;
};
''%%TypeID%%'' is used to assign appropriate action for this entity and/or locate the appropriate sprite sequence or model to draw. If ''%%TypeID%%'' is zero, it means it’s playable character (i.e. Lara).
''%%Room%%'' is a room ID to which this particular entity belongs to. If //room// value was modified incorrectly, entity will glitch and, most likely, won’t appear in engine. That is, you can’t change entity position without complementary edit or ''%%Room%%'' field.
''%%Angle%%'' is an //Euler Yaw angle// (i.e. “horizontal” rotation) stored in a special manner. To convert it to ordinary degrees, use this formula:
$\angle^\circ = \frac{Angle}{16384} \cdot -90$
{{:icons:tr1.png?nolink|TR1 only}} ''%%Intensity2%%'' field is missing in this game version, so the structure size is 2 bytes less.
''%%Intensity1%%'': If not -1, it is a value of constant lighting. -1 means “use mesh lighting”.
''%%Flags%%'' value contain packed list of several parameters:
* **Bit 7** (''%%0x0080%%'') — {{:icons:tr1.png?nolink|TR1 only}}{{:icons:tr2.png?nolink|TR2 only}}{{:icons:tr3.png?nolink|TR3 only}} //Clear Body// flag. It is used together with //Clear Bodies// trigger action to remove the body of dead enemy from the level to conserve resources.
* **Bit 8** (''%%0x0100%%'') — //Invisible// flag. If entity has this flag set, it will be invisible on start-up. However, it only works for specific types of entities. It is primarily used with pick-ups or other entities which should appear at certain point only after activation, but are visible by default.
* **Bits 9..13** (''%%0x3E00%%'') — //Activation Mask// for this entity. As you already learned in //Trigger Actions// chapter, entity is only activated when //activation mask is all set// (i.e. all 5 bits are set, and value is ''%%0x1F%%''). However, activation mask doesn’t strictly required to be set by trigger — level editor allows to //pre-define// activation mask, so entity will bear specific activation mask layout on level start-up.
If activation mask was pre-set to ''%%0x1F%%'' (all set), entity will activate //immediately after level loading//, and engine will also //reset activation mask to zero// and //mark entity as inactive//, effectively swapping “inactive” state with “active”. That is, when player will activate such pre-activated entity with a trigger, it will actually “deactivate”, et cetera. Most prominent example of this behaviour is pre-opened grated door in Tomb of Qualopec.
{{anchor:object-code-bit}}
==== Object Code Bit ====
In TR4 and TR5, ''%%Intensity2%%'' field was replaced with completely new one, called //Object Code Bit// (or ''%%OCB%%''). OCB allows to alter entity behaviour based on its value, thus providing very basic “script-like” functionality. For example, flame emitter entities have a case switch for OCB value, and each valid OCB value produces different result — flame emttier acts either as a static flame, as a directional flame, as a lightning, and so on.
More detailed description of OCB is provided in [[trs:npc_behaviour|this section]].
===== Sprites =====
These are “billboard” objects that are always rendered perpendicular to the view direction. These are used for text and explosion effects and similar things; they are also used for some scenery objects and pickup items, though this use gets less as one goes from TR1 to TR3. The various “Sides” below are the positions of the sprite sides relative to the sprite’s overall position, measured in TR’s world-coordinate units.
{{anchor:tr_sprite_texture}}
struct tr_sprite_texture // 16 bytes
{
uint16_t Atlas;
uint8_t x;
uint8_t y;
uint16_t Width; // (ActualWidth * 256) + 255
uint16_t Height; // (ActualHeight * 256) + 255
int16_t LeftSide;
int16_t TopSide;
int16_t RightSide;
int16_t BottomSide;
};
{{:icons:tr4.png?nolink|TR4 only}}{{:icons:tr5.png?nolink|TR5 only}} ''%%x%%'' and ''%%y%%'' values //are not used// in this version. Additionally, formula for ''%%Width%%'' and ''%%Height%%'' is changed: now it’s $(\text{ActualWidth} - 1) \cdot 256$ and $(\text{ActualHeight} - 1) \cdot 256$ respectively.
===== Sprite Sequences =====
These are collections of sprites that are referred to as a group. The members of this group can be cycled through (animated sprites such as flames, blood splats or explosions) or selected in other ways (text). Some sequences have only one member; this is done so as to access all the sprites in the same way.
{{anchor:tr_sprite_sequence}}
struct tr_sprite_sequence // 8 bytes
{
int32_t SpriteID; // Sprite identifier
int16_t NegativeLength; // Negative of ``how many sprites are in this sequence''
int16_t Offset; // Where (in sprite texture list) this sequence starts
};