HowTo/Build a cab interior

From TrainzOnline
< HowTo
Revision as of 17:39, 8 October 2012 by Windwalkr (Talk | contribs)

Jump to: navigation, search

Trainz uses an interior object to represent a locomotive cab, or view from another vehicle, e.g. a passenger coach, brake van or caboose. Interiors can also be attached to some non-train assets. For this howto, I am going to assume that we are creating a traincar interior, and focus primarily on a cab interior for a diesel or electric locomotive.

Contents

Basic structure

The basic structure of a cab is quite similar to a number of different assets. You will have a main mesh with a number of attached sub-meshes, which are arranged using a mesh-table.

The main cab mesh typically consists of the interior walls, roof and floor of the cab, the control desk or stand, all furniture or other static objects visible inside the cab, and also all parts of the locomotive exterior that are visible from inside the cab - e.g. the top of any projecting nose below the cab windows. Remember to consider the sightlines out of rear windows too for visible bodywork panels - the user can move around the cab and rotate the camera to any angle. Users are not restricted to looking forward out of the front windows only.


Artwork considerations

There are some differences in style between the construction of an interior object and a typical trainz object.

Except in very specific circumstances, there will not be more than one interior object in existence at any time, and that interior is always viewed from very close range - the user is inside it. Because of this, interiors are typically modelled with a high polygon count and with high resolution textures, to give an impressive visual feel. LOD is not used, as the user never gets far enough away from the interior for LOD techniques to have worthwhile benefit.

Trainz lights interior objects differently to map objects like buildings and other scenery - the main illumination is ambient, not directional. (A steam loco does have a directional light coming from the firebox, but a diesel or electric cab typically has no directional light inside, and relies entirely on ambient illumination).

With ambient illumination, effects such as normal and specular mapping do not have much (if any) effect. Because of this, more effort needs to be put into the diffuse texture for a cab interior than would normally be applied to Trainz objects. Drawing or baking shadows and other effects into the diffuse textures is required to produce a nice looking cab interior.


A basic config file

The minimal required config for an interior to show in-game is:

kind                                    interior
kuid                                    <kuid2:YOUR:KUID:HERE>
trainz-build                            2.9
username                                "Your interior name here"
category-class                          ZI

mesh-table
{
  default
  {
    mesh                                "your_interior_mesh_name.im"
    auto-create                         1
  }
}

Note: There are also other base tags that should be added that are common to all asset types - e.g. the download station will require a valid thumbnails container before the asset can be uploaded. See the documentation for KIND TrainzBaseSpec for details of tags which apply to all trainz assets. Depending on which Trainz version you have and which trainz-build number you have specified, some of these may be required - content manager will tell you what is required, listing missing tags as errors.

camera positions

If we load a cab up with that config file, we will note that the view is not particularly appealing. Chances are we're right down near the floor. To fix this, we need to define some default positions for camera views. This is done with the cameralist container and the cameradefault tag. Add the following to your config.txt file:

cameralist
{
  camera0                               0,0,2,0,0
}
cameradefault                           0

The cameralist container can contain any number of cameras, but they must be numbered sequentially and start at zero.

The five numbers are the X, Y and Z coordinates of the camera in metres relative to the 0,0,0 point of the interior mesh, and the Yaw and Pitch in radians. Luckily you don't have to work these values out from scratch - add the line "-freeintcam" to your trainzoptions.txt file, and you can move the camera about in the cab with the mouse and keyboard, and the precise position will be displayed in the bottom right corner.

When you have found a position you like, write the numbers down. When you've got a good selection of positions recorded, add them to the cameralist container.

You may wish to return to this and tweak the values later once all the controls are in place - but it's handy to have a good starting position for the camera while testing.

Cab controls

A locomotive cab interior needs working cab controls. What fun would sitting in the drivers seat be without being able to pull on the levers, change the switches, and watch the gauges and dials move?

The moving part of each lever, switch, gauge or dial is created as a separate mesh, which is then attached to the cab with an attachment point. (The static part, e.g. guide rails for levers, should be part of the cab mesh itself).


Lever

The lever is the basic input device in a cab. It is based on rotational movement, and any object that requires rotational movement (e.g. a wheel or ring) can be implemented as a lever.

If you actually want linear movement, you can still use a lever - just move the attachment point a long way away - e.g. 10 or 20 metres away from the control. The resulting small arc will not be noticable over the length of movement. As an example, sliding windows in Trainz cabs have been implemented as levers "attached" something like 10 or 20 metres out to the side of the vehicle for a long time. (The exception to this is a horn pull rope - trainz provides a control specifically for this purpose).


You will need to create the lever in your modelling program aligned correctly - the rotation needs to be around the Z axis, and main part of the object will point along the Y axis.

When adding the attachment point to the main mesh, align it so the Z axis is the axis of rotation. It is common to point the Y axis at the default position - e.g. levers in the 'off' or 'neutral' position, and needles pointing to zero. This is not strictly required, as the range of movement can be adjusted at both ends, but it provides a handy reference when creating the config file.

It is worth noting that you can reuse a mesh for multiple attachments. This is particularly useful for switches, as identical appearance switches are often used repeatedly in a cab for different purposes.


Throttle Lever

A good example of a standard lever in use is a locomotive throttle control. A typical US diesel throttle has 9 notched positions (from 0 to 8 inclusive).

As noted above, the lever is created in the modelling package so it points along the Y axis and the axis of rotation is along the Z axis. The attachment point is added to the cab interior mesh at the correct position, and rotated so the Z axis is aligned with the axis of rotation, and the Y axis points to the default position.

Now we need to add an entry to the config file to add our throttle lever.

Into the mesh table, add an entry like this:

  throttle_lever
  {
    kind                                "lever"
    mesh                                "throttle_lever.im"
    att                                 "a.throttle_lever"
    limits                              0,8
    angles                              0,1.57
    notches                             0,0.125,0.25,0.375,0.5,0.625,0.75,0.875,1
    notchheight                         1,2,2,2,2,2,2,2,1
    radius                              0.35
    auto-create                         1
    att-parent                          "default"
  }

... remember to set your attachment point and mesh name correctly.

The 'angles' line defines how far the control rotates. It is in radians - there are 2 x Pi (6.28) radians in a complete circle. My default of 1.57 will give you about 90 degrees of rotation, in the positive direction. Adjust this number to get the control to rotate the correct amount. You should be able to work out the number you need in your modelling program by using the rotate tool on the attachment point, and recording the amount of rotation required to move to the intended maximum location. It should not require significant trial and error.

The 'radius' line defines the position of the notch position overlay and the text label. It is in metres. If the overlay is too close in, increase this number. If it is too far out, decrease it.

If the lever is the wrong way round (notch 8 is at the wrong end), swap the numbers in the 'angles' tag over, so the higher one is first.

If the lever moves the wrong way when you drag it with the mouse (i.e. it goes left in response to a right drag), add the line:

    mousespeed                          -1

This will invert the mouse movement.

The fact we called this lever "throttle_lever" means it will be used to control the throttle by the default cab script.

So what happens if we wanted a different number of notches? Below is an example of a throttle which has 22 notches. The second value in the 'limits' tag has been edited to return up to notch 22, and more entries have been added to the 'notches' and 'notchheight' tags:

  throttle_lever
  {
    kind                                "lever"
    mesh                                "throttle_lever.im"
    att                                 "a.throttle_lever"
    limits                              0,22
    angles                              0,0.94
    notches                             0,0.045,0.091,0.136,0.182,0.227,0.273,0.318,0.364,0.409,0.455,0.5,0.545,0.591,0.636,0.682,0.727,0.773,0.818,0.864,0.909,0.955,1
    notchheight                         1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1
    radius                              0.35
    auto-create                         1
    att-parent                          "default"
  }


Reverser Lever

The reverser lever is created in a similar manner to throttle lever. The mesh table entry for this will look like:

  reverser_lever
  {
    kind                                "lever"
    mesh                                "reverser_lever.im"
    att                                 "a.reverser_lever"
    limits                              0,2
    angles                              -0.79,0.79
    notches                             0,0.5,1
    notchheight                         1,1,1
    radius                              0.25
    auto-create                         1
    att-parent                          "default"
  }

In this case, because the 'angles' entry has a range both positive and negative, the lever can move in both directions relative to the attachment point. The game will set the default position to neutral (1), so if you've lined up your attachment point at one end, don't worry - you can use angles which range from zero if you need to.


Train Air Brake Lever

There are two types of train air brake lever. A given locomotive only has one type - the brakes are either require manual lapping or are self-lapping. Use the appropriate name and configuration for the loco you are modelling.

Manual Lapped Brakes

The manual train brake lever has five positions numbered from zero to four, each one representing a different function. Those functions are Release, Lap, Application, Emergency and Handle Off respectively.

A lever for a manual lap train air brake is defined as follows:

  trainbrakelap_lever
  {
    kind                                "lever"
    mesh                                "train_brake_lap_lever.im"
    att                                 "a.train_brake_lap_lever"
    limits                              0,4
    angles                              0,0.94
    notches                             0,0.25,0.5,0.75,1
    notchheight                         1,1,1,1,1
    radius                              0.25
    att-parent                          "default"
    auto-create                         1
  }

In this case, the attachment for the lever has been created in the 'release' position. If it had been created in the 'handle off' position, the angle tag would have been more like:

    angles                             -0.94,0


Self Lapping Brakes

A self lapping trainbrake lever has a number of primary notches similar to the manual lap train brake lever. However, instead of a 'lap' and 'apply' position, it has a grade of application between the 'initial' and 'full application' positions. An example of a self-lapping brake handle is this one:

  trainbrake_lever
  {
    kind                                "lever"
    mesh                                "train_brake_lever.im"
    att                                 "a.train_brake_lever"
    limits                              0,4
    angles                              0,0.94
    notches                             0,0.25,0.27,0.29,0.31,0.33,0.35,0.37,0.39,0.41,0.43,0.45,0.47,0.49,0.5,0.75,1
    notchheight                         1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
    radius                              0.25
    att-parent                          "default"
    auto-create                         1
  }

In this case, there are 14 possible levels of brake application - initial at 0.25, 12 intermediate levels, and full application at 0.5. There is no restriction on the number of intermediate levels - if you are modelling a prototype brake handle with a specific number, you may create precisely that number of notches and arrange them between 0.25 and 0.5 as required.


Combined throttle brake lever

There is also another control option - a combined throttle brake lever. This type of control is often found on multiple unit trains. This control is split into two halves. The first half of the movement is an inverted throttle - i.e. max power at zero, no power at 0.5. The second half is an auto lapped train brake application, with 0.5 as release, and 1.0 as emergency. An example of how to configure a combined throttle and brake lever is below:

  throttle_brake_lever
  {
    kind                                "lever"
    mesh                                "throttle_brake_handle.im"
    auto-create                         1
    att                                 "a.throttle_brake_handle"
    limits                              0,16
    angles                              0.94,-0.94
    notches                             0,0.0625,0.125,0.1875,0.25,0.3125,0.375,0.4375,0.5,0.5625,0.625,0.6875,0.8125,0.75,0.875,0.9375,1
    notchheight                         1,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,1
    att-parent                          "default"
    radius                              0.12
    invert                              1
  }

Unfortunately, trains that use this type of control generally use blended and/or EP brakes, and this control uses the standard Trainz air brake, which operates in a distinctly different manner, and as a result, can feel very wrong for this control type. (See the section on scripting controls for how a better combined brake and throttle lever may be implemented).


Pull lever

This is a sprung lever. You pull on this lever to activate it, and when you let it go, it returns to the original position. This kind of action is commonly associated with a locomotive horn, and this is what I am using as an example:

  horn
  {
    kind                                "pulllever"
    mesh                                "horn.im"
    att                                 "a.horn"
    auto-create                         1
    angles                              0,-0.55
    limits                              0,1
    mousespeed                          -1
    radius                              -0.02
    notches                             0,1
    notchheight                         0,0
  }


Switch

A switch is a lever that does not require the user to 'drag' it with the mouse - it operates on a click only. This is used for smaller controls that are 'on' or 'off', and do not have intermediate positions. Parameters are much like they are for a 'lever', only a switch should only have two notches - on and off.

Unfortunately, some things you'd normally associate with being a 'switch' (e.g. the headlight switch control) require mulitple positions, and will need to remain as a 'lever' in order to allow the player to set the correct position for the control.

An example of a switch would be:

  switch1
  {
    kind                                "switch"
    mesh                                "switch.im"
    att                                 "a.switch1"
    limits                              0,1
    angles                              1.4,0
    notches                             0,1
    notchheight                         0,0
  }


As 'switch' was introduced in TS2009 SP2, interiors that use kind 'switch' controls should be marked trainz-build 3.1 or later in the config.txt.

button

A button is a momentary contact press-and-hold button. This is a brand new control added for TS12 patch 2, released end of june 2011. Content using this feature should have trainz-build 3.6 or later in the config.txt.

Finally this brings Trainz the "click to press", "hold to hold pressed", "let go to release" style momentary pushbutton.

  button1
  {
    kind                                "button"
    mesh                                "switch.im"
    att                                 "a.switch1"
    limits                              0,1
    angles                              0,0.1
    notches                             0,1
    notchheight                         0,0
  }


Pull rope

This is a linear pull rope control. Unlike the rotating controls discussed previously, this control type moves the mesh linearly along the Z axis of the attachment. It behaves as you would expect from a suspended rope - click and drag to pull, let go to release. It will spring back to it's initial position in the same manner as kind 'pulllever'.

  horn
  {
    kind                                "pullrope"
    mesh                                "horn_rope.im"
    att                                 "a.horn"
    limits                              0,1
    angles                              0.1,0
    notches                             0,1
    notchheight                         0,0
    mousespeed                          -1
  }


needle

A needle is the standard Trainz output device. Like the lever, it is based on rotational movement, and any gauge, dial, or other output device based on rotational movement can be implemented. Any static part of the gauge should be modelled as part of the cab, and only the moving part as part of the needle. Like the lever, the rotation will be around the Z axis of the needle, and it is common practice to point the point of the needle along the Y axis. Cabs often have a number of similar gauges, and re-using the same needle repeatedly in each gauge is a lot easier if this setup is done consistently.

As an example of a needle, here is a basic speedometer:

  speedo_needle
  {
    kind                                "needle"
    auto-create                         1
    mesh                                "speedo_needle.im"
    att                                 "a.speedo_needle"
    limits                              0,67.056
    angles                              0,4.712
  }

The default cab script knows to move the "speedo_needle" object as the train speed changes, so simply by naming this control "speedo_needle", it has become a speedometer.

For a speedo, the 'limits' values will be the minimum and maximum reportable speed in m/s.


Creating other rotational gauges is similar - refer to the mesh-table (interior version) documentation to see all the different values that Trainz can show by default.


light

Another common type of control is a light. This is created simply as a textured plane, which is either hidden or shown to represent light off or light on. It is attached as any other standard mesh attachment would be - there is no rotation to worry about here.

  wheelslip_light
  {
    kind                                "light"
    mesh                                "wheelslip_light.im"
    att                                 "a.wheelslip_light"
  }

This light will be used to show wheelslip in a default cab.

animated-lever

If you want more complex movement than simple rotation, you have the option of animating the lever and using kind 'animated-lever'. This will use an animation you create in your modelling program to move the control about. An example of this is visible in the MN ACMU cab. (I've reduced the number of notches here for readability - the real one has 32, not 16).

  throttle_lever
  {
    mesh                                "throttlehandle.im"
    anim                                "throttlehandle.kin"
    auto-create                         1
    att                                 "a.throttlelever"
    limits                              0,16
    notches                             0,0.0625,0.125,0.1875,0.25,0.3125,0.375,0.4375,0.5,0.5625,0.625,0.6875,0.8125,0.75,0.875,0.9375,1
    notchheight                         1,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,1
    att-parent                          "default"
    kind                                "animated-lever"
    test-collisions                     0
    mousespeed                          1
  }

Where the standard lever is rotated between it's angles to move from notch to notch, the animated-lever has it's animation played to the corresponding frame.


collision-proxy

Animated levers can be hard to grab hold of compared to normal levers. To get around this, an invisible mesh is used to show the game the range of movement the control has, and to allow the user to grab the lever more easily. This example again comes from the MN ACMU cab:

  throttle-collision-box
  {
    mesh                                "throttlehandleselection.im"
    att-parent                          "throttle_lever"
    att                                 "a.selection_box"
    auto-create                         1
    kind                                "collision-proxy"
    opacity                             0
    collision-parent                    "throttle_lever"
  }

animated-dial

If you've been paying attention to levers, needles and animated levers, I think you can guess where we are going with this one. This is an animated needle. Instead of rotating between the angles specified, it plays it's animation to the relevant frame to show the value. Use this when what you want to achieve can't be done with simple rotational movement.

(Example usage for this control to follow).

digital-dial

Some locomotives use a digital readout rather than analogue dials.

Trainz has two control kinds to support digital displays - one for speed units, and one for pressure units. Instead of angles of movement, these both have some font specific options to configure how the digits appear.

"font" defines the typeface. For a list of valid typefaces, see the mesh-table (interior version) documentation. Generally this follows closely with a number of default windows font names.

"fontsize" defines the size of the font. The default value for this is 0.02, and this is a pretty respectable size display.

"fontcolor" defines the RGB color of the text. Each value is a floating point number between 0.0 and 1.0.


digital-dial-spd

For a speedometer (or any other gauge that shows units in speed, e.g. a speed limit display), use kind 'digital-dial-spd'.

An example speedometer using this kind would be declared as:

  speedo_needle2
  {
    kind                                "digital-dial-spd"
    att                                 "a.digispeedo"
    font                                "arial"
    fontsize                            0.01
    fontcolor                           1.0,1.0,1.0
    auto-create                         1
  }

digital-dial-prs

For a display using pressure units, use digital-dial-prs. This is a digital brake pipe pressure gauge:

  bptrainbrakepipe_needle2
  {
    kind                                "digital-dial-prs"
    att                                 "a.trainpipe_pressure"
    font                                "arial"
    fontsize                            0.01
    fontcolor                           0.0,1.0,0.0
    auto-create                         1
  }
 

firebox

The firebox control is a special kind to implement the fire of a steam locomotive.

(Further information about this specific control to follow).


Scripting additional behaviour in a cab

A lot can be achieved in Trainz purely by naming the controls correctly and relying on Trainz default cab scripts to make them work. A quite reasonable cab can be made for many locomotives without having to do any scripting whatsoever.

However, a lot more realism can be achieved by scripting the cab. Additional features can be added beyond what Trainz supports, including new types of controls, or extended behaviour on existing controls.

The first thing to consider when looking at scripting a cab, is "is the cab the best place for this feature?". A cab is not a persistent asset, which means it can go away at any point. When it's gone, it isn't running it's script. Consequently, it is a bad place for any persistent or time based logic - it should only be used to script the immediate behaviour of the controls. Anything beyond this should be implemented the vehicle itself or possibly a script library, both of which are more persistent objects.

If all you want to achieve is to add some simple extra features (e.g. a display of signal aspects within the cab), you can derive from the trainz standard classes, and inherit the default behaviour when adding your new behaviour. This means you can concentrate on just your extra feature, and don't have to worry about making existing functionality work.

If you want to do something a little more advanced (e.g. change the behaviour of the throttle and/or brake controllers), then you are probably best deriving your cab script from the 'cabin' class, ignoring the default cab scripts, and implementing your required behaviour from scratch. This gives you a lot more control, but you do need to do more yourself.

For some examples of the first route, have a look at the MN cabs - the M7 or the ACMU are good examples. Both have a cab signal repeater implemented in the cab interior script, and use the base functionality for almost everything else.

-~= To Be Continued... =~-

Personal tools