Table of Contents

Model Definition (.mdl)

Models are stored in the ​.mdl​ format in the ​res\models\model\​ folder. The format consists of geometric information for different levels of detail (LODs) and metadata. For each LOD, there is a separate hierarchy of meshes with their animations, events, and materials. In general, ​.mdl​ files contain a ​data()​-function that returns a struct similar to the one below:

function data()
return {
  boundingInfo = { ... }, -- optional bounding box used e.g. for render borders
  collider = { ... },     -- optional collider for collision calculation
  lods = { ... },         -- geometric information for 3D data and textures
  metadata = { ... },     -- metadata depending on model type
  version = 1,            -- new Transport Fever 2 lod tree format
}
end

Bounding Box

The bounding box is a cuboid that encloses the outermost elements of the model so that all parts of the model lie within the box. It is used, for example, to decide whether an object is within the visible range of the camera or not.

  boundingInfo = {
	bbMax = { <+x>, <+y>, <+z>, },
	bbMin = { <-x>, <-y>, <-z>, },
  },

The values are the distances from model origin in positive and negative direction on all three axis.

Collider

The collider is used whenever the potential collision between models is considered. It's possible to use the mesh data for collision calculation as well as define the collision boundaries by script.

Collider from Mesh

When the mesh data should be used for collision calculation,​ the type “​MESH”​ is needed:

  collider = {
	params = { },
	transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
	type = "​MESH",},
Collider from script

When the boundaries should be set by script, the type “​BOX”,​ “​CYLINDER”​ or “​POINT_CLOUD”​ is needed:

  collider = {
	params = {
	  halfExtents = { 1.5, 1.5, 1.5, },
	},
	transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
	type = "​BOX",},

The size of the volume is specified by 2 times the ​halfExtents​ properties for the “​BOX”​ and “​CYLINDER”​ type. It can be offsetted relative to the model origin with the ​transf​ parameter. The “​POINT_CLOUD”​ uses a list of points that is provided in the points parameter. Each point has three values for the position on all three axis relative to the construction origin. The ​transf​ parameter is ignored. ​

Level of Detail

Levels of detail (LOD) are used to use simplified geometries with less tris as the distance between model and camera increases, thus saving computing power during rendering. At least one LOD is needed, but it is recommended to include more if the model has a large number of tris.

  lods = {
	{
	  node = { ... }
	  static = false,
	  visibleFrom = 0,
	  visibleTo = 200,
	},
	...
  },

The node element is the top level of the mesh hierarchy in this LOD. The range of visibility is limited by ​visibleFrom​ and ​visibleTo​.

Mesh hierarchy

The hierarchy is defined by nesting of nodes. The root node is the toplevel parent node.

Nodes

A node can have the following attributes:

  {
    children = { ... }, -- optional, contains more nodes
    transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, -- transformation matrix for size, ...
    animations = { ... } -- optional, for details see below
    mesh = "​path/​to/​mesh.msh"-- optional, relative to res/​models/​mesh/​
    materials = { ... } -- needed if mesh is used, for details see below
    name = "​mesh_01"-- optional, can be used as reference elsewhere
  }

Animations

The animations block contains a list of animations with eventnames as keys. Each of these animations is either defined by keyframes or file based.

Example code for a keyframe and a file based animation:

  animations = {
    eventname1 = {
      forward = true, -- or false for inverse keyframe playback
      params = {
        keyframes = {
          {
            rot = { 0, 0, 0, },    -- rotation around all three axis
            time = 0,              -- milliseconds since begin of animation
            transl = { 0, 0, 0, }, -- position offset for all three axis
          },
          ...
          {
            rot = { 0, 0, 0, },
            time = 1200,
            transl = { 0.75, 0.055, 0, },
          },
        },
        origin = { 0, 0, 0, }, -- offset of animation origin from node origin
      },
      type = "​KEYFRAME",},
    eventname2 = {
      params = {
        id = "​path/​to/​animation.ani",-- relative to res/​models/​animation/​
      },
      type = "​FILE_REF",},
  },

Further information on animation files can be found in the animation section.

Meshes and Materials

Nodes can contain a mesh. Meshes consist of two files, ​meshname.msh​ with index information and ​meshname.msh.blob​ with 3D model data (binary). See the documentation of msh files for further information on meshes.

For each submesh provided by the referenced ​.msh​ file, a material has to be referenced. ​

  materials = {
    "​path/​to/​material.mtl",-- paths relative to res/​models/​material/​
    "​path/​to/​secondMaterial.mtl",...
  }

Further details about material files and types can be found in the material documentation. ​

Metadata

There is large set of metadata keys to describe all the different types of models in the game. Some of the keys can be used for almost every type of model. These are described below. For specific model types, the keys are described in the relevant sections of the wiki:

Universal Keys

These metadata keys can be applied to any or the majority of model types. Depending on the resource type, they might be mandatory, but can often be left empty to default to zero.

Description

The description provides the name and a descriptive text for the model. It is used for vehicle informations in buy menu and status windows. Static models that are not part of a construction use it for the label in the menu. In constructions, these information are not used.

    description = {
      name = _("Name of Model"),
      description = _("Description displayed for example in the depot menu")
    },

Availability

The availability defines the timespan in which the model is available. yearFrom and/or yearTo can be left out or set to zero to define an indefinite start respectively end year.

    availability = {
      yearFrom = 1925,
      yearTo = 1985
    },

Cost

To activate automatic price calculation, set the price to -1. When price is left out, it defaults to 0. The value of priceScale can be used to adjust the calculated price. This is a hook up point for balancing mods too.

    cost = {
      price = 10000,
      priceScale = 1
    },

Maintenance

To activate automatic maintenance calculation, set the running cost to -1. When runningCost or lifespan is left out, it defaults to 0. The value of priceScale can be used to adjust the calculated price. This is a hook up point for balancing mods too.

    maintenance = {
      runningCosts = -1,
      runningCostScale = 1,
      lifespan = 40 * 730   -- [1 unit at normal game speed corresponds to 12h, a year equals lifespan = 730]
    },

ParticleSystem

Particle emitters produce smoke or steam particles.

    particleSystem = {
      emitters = {
        {
          child = 1,
          position = { 4.3632001876831, 0, 3.8589000701904, }, 
          color = { 0.35, 0.35, 0.35, },
          frequency = 80,
          lifeTime = 14,
          size01 = { 0.80000001192093, 1, }, 
          velocity = { 0, 0, 10, },
          initialAlpha = 0.8,
          velocityDampingFactor = 2.0,
        },
        ...
      },
    },

A model can have zero or more particle emitters. Each of the emitters has its own block in the emitters list. It has several properties:

Particles are supported for all vehicle types as well as static construction models support the particle emitters.

CameraConfig

By default vehicles, animals and people have an onboard camera that is relative to the node with the first crew seat or if no crew member is available, it is relative to the position and rotation of node 0. The cameraConfig allows for setting multiple custom camera positions, which can be cycled when the onboard camera is active.

    cameraConfig = {
      positions = {
        {
          group = 0,
          transf = transf.rotYCntTransl(math.rad(25), vec3.new(0, 0, 0), vec3.new(-15, 0, 9)),
          fov = 90
        }
      }
    }

For every camera position, there is a block in the positions list with several parameters:

Label List

In Transport Fever 2, vehicles and constructions may display some dynamic text labels. These are defined in a labelList block in the model metadata which contains a labels list:

  ..
  labelList = {
    labels = {
      {
        type = "LINE_NAME",
        transf = { 0, -1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, -5.8043 , 0.1814, 2.808 , 1, },
        size = { 0.366, 0.210 },
        color = {247 / 255, 147 / 255, 33 /255},
        fitting = "CUT",
        alignment = "CENTER",
        filter = "NUMBER",
        renderMode = "EMISSIVE",
        childId = "RootNode",
      },
      ...
    },
  },
  ..

There are many different properties which can be used to define the labels.

To set the position of the label, use the transf property pointing to the coordinates where the lower left corner of the label should be. The label will be placed aligned to the X and Z axis. The coordinates are relative to the mesh that is referenced in childID by name.

The size contains a pair of two values. The first for the size in x direction, the second for the size in y direction, both relative to the transf. Negative values will result in no text visible. The y direction is used for the fontsize too. To use the label with more than one line of text, set nLines to a value larger than 1. Values below 1 are ignored.

Coloring the text is possible by using the color attribute. It receives a vector with three values, one for each color in the range between 0 and 1. The transparency of the text is set by alpha. Value 0 is invisible, value 1 is opaque, values greater than 1 might lead to artifacts. With the alphaMode, it is possible to define how the alphablending is done. Possible values are either “CUTOUT”, “BLEND” or “NONE”. In Front of opaque textures, this might be irrelevant, but on transparent faces like glass panes it might be more relevant. To get emissive text (like with LCD destination displays), set the renderMode to “EMISSIVE”, otherwise set it to “STD”.

The horizontal alignment of the text can be set in alignment with the values “LEFT”, “CENTER” and “RIGHT”. For the vertical alignment, set verticalAlignment either to “BOTTOM”, “CENTER” or “TOP”.

To adjust the behavior of text that is longer than the label, set fitting either to:

The type property contains one of the following keys:

The filter property is used to filter the input from the type. It can either be set to NONE to not filter, NUMBER to filter anything that is no number or CUSTOM to set advanced filters in the params block. It contains advanced parameters to influence the text that should be displayed:

The font parameter currently only supports the vanilla fonts Lato and Noto. Other fonts than the standard ones lead to crashes.

It is recommended to test the labels with the model editor.

Transport Network Provider

Models (except vehicles) can contain lanes and terminals for vehicles, cargo and people. These are configured in the transportNetworkProvider metadata block of the model.

The transportNetworkProvider contains three different properties:

transportNetworkProvider = {
  laneLists = {
    {
      linkable = false,
      nodes = {
        { { 0, -20, -2.1, }, { 0, 20, 0, }, 30, },
        { { 0, 0, -2.1, },   { 0, 20, 0, }, 30, },
 
        { { 0, 0, -2.1, },   { 0, 20, 0, }, 30, },
        { { 0, 20, -2.1, },  { 0, 20, 0, }, 30, },
      },
      speedLimit = 20,
      transportModes = { "SHIP", "SMALL_SHIP" },
    },
    ...
  },
  runways = {
    {
      edges = { 0, },
      node = 0,
      type = "LANDING",
    },
    {
      edges = { 1, },
      node = 3,
      type = "TAKEOFF",
    },
  },
  terminals = {
    {
      order = 0,
      personEdges = { 2, 3, },
      personNodes = { 5, 8, },
      vehicleNode = 2,
    }, 
  },
},

The laneLists is a list of blocks which have several properties each:

The nodes can be referenced with their index over all lists. The edge 0 is defined by node 0 and 1, edge 1 is defined by node 2 and 3, edge n is defined by node 2×n and 2×n+1.

The runways block contains 0 or more runway blocks which have several properties related to the entry and exit points of the freely moving planes and ships:

An aircraft tries to land and slow down in front of the landing node. After passing it, it will taxi to the terminal. On departure, it will speed up and take off after passing the takeoff node. The landing/takeoff direction is given by the tangent of the first/last edge in the edges list. If the runway is not longSame holds for ships.

The terminals block contains a list of terminal definition. At a terminal, cargo items and passengers can enter and leave vehicles. Each terminal block contains the following parameters: