Introduction to JBeam

Overview

JBeam is the file format that defines the physics skeleton in the BeamNG engine. It is called JBeam as it is based on JSON (with some exceptions) in order to define node/beam constructs.

Just about everything is case sensitive, and it is not at all friendly to syntax errors, so be careful! You create and edit jbeam files using a text processing program (such as Notepad++, VSCode, etc).


Difference to JSON

We modified the JSON parser a bit to make the life of the vehicle authors easier:

  • Comments: C-style, multi-line, and single-line comments are supported: //... and /* ... */
  • Commas: All commas are optional, but it is advised to only omit the commas at the end of lines.

General Structure

{
  "vehicle": {
    "refNodes":[
        ["ref:", "back:", "left:", "up:"]
        ["f3r", "f5r", "f4l", "f8r"]
    ],
    "cameraExternal":{
      "distance": 6.7,
      "distanceMin": 9,
      "offset": {"x": 0.43, "y": 0.11, "z": 0.55},
      "fov": 77,
    },
  }
}

General Concepts

The data in a jbeam file can be categorized into two types of formatting:

Tables

Think of a a spreadsheet table: Lists are always having a header row that defines the column names and then the data follows. For example:

{
  "vehicle": {
    "nodes": [ /* ... */ ],
    "refNodes":[
      ["ref:", "back:", "left:", "up:"],
      ["f3r", "f5r", "f4l", "f8r"],
      ["f2r", "f3r", "f1l", "f1r"]
    ]
  }
}

So in above example, f3r is the first value in its row in the column ref: of the table refNodes that is contained in vehicle. Think of it this way:

Tables are used when you have a lot of the same data, so you save space specifying the key over and over again.

After parsing, the format is transferred into a key-value storage and every row is matched with the coumn headers. For above example turns into this for the engine:

{
  "vehicle": {
    "nodes": [ /* ... */ ],
    "refNodes": [
      {
        "ref:nodes": "f3r",
        "back:nodes": "f5r",
        "left:nodes": "f4l",
        "up:nodes": "f8r",
      },
      {
        "ref:nodes": "f2r",
        "back:nodes": "f3r",
        "left:nodes": "f1l",
        "up:nodes": "f1r",
      }
    ]
  }
}

Section links

The colon (:) in the header row of a table specifies a link to another section of the vehicle. The format is as follows: valueName:targetSectionName. If targetSectionName is omitted, it will be nodes.

For example the table header in row 15:

 1{
 2  "vehicle": {
 3    "nodes": [
 4      ["id", "posX", "posY", "posZ"],
 5      ["f3r", -0.35, -1.56, 0.25],
 6      ["f5r",  0.00, -1.58, 0.24],
 7      ["f4l", -0.37, -0.98, 0.44],
 8      ["f8r", -0.37, -0.98, 0.22],
 9      ["f2r",  0.37, -0.98, 0.22],
10      ["f3r",  0.37, -0.98, 0.44],
11      ["f1l", -0.69, -0.62, 0.39],
12      ["f1r", -0.65, -0.64, 0.22]
13    ],
14    "refNodes":[
15      ["ref:", "back:", "left:", "up:"],
16      ["f3r", "f5r", "f4l", "f8r"],
17      ["f2r", "f3r", "f1l", "f1r"]
18    ]
19  }
20}

In above example, with "ref:nodes": "f3r" the engine would reference to id = f3r in the nodes table.

Dictionaries

These things are key-value data storages. They are used when the data is quite unique and does not repeat itself so much.

{
  "vehicle": {
    "cameraExternal": {
      "distance": 6.7,
      "distanceMin": 9,
      "offset": {"x": 0.43, "y": 0.11, "z": 0.55},
      "fov": 77,
    },
  }
}

There is no post-processing required with dictionaries, but they are seldomly used.

Out-of-bounds modifiers

There are a lot of input variables that the engine looks for, so we added a way to modify certain values.

Scope modifiers

For example:

 1{
 2  "vehicle": {
 3    "nodes": [
 4      ["id", "posX", "posY", "posZ"],
 5      ["f3r", -0.35, -1.56, 0.25],
 6      ["f5r",  0.00, -1.58, 0.24],
 7      ["f4l", -0.37, -0.98, 0.44],
 8      ["f8r", -0.37, -0.98, 0.22],
 9      {"nodeWeight": 3},
10      ["f2r",  0.37, -0.98, 0.22],
11      ["f3r",  0.37, -0.98, 0.44],
12      ["f1l", -0.69, -0.62, 0.39],
13      ["f1r", -0.65, -0.64, 0.22]
14    ],
15  }
16}

The line 9 in above example is certainly not an ordinary table row. (Any rows need to be with [ ]). It will add the key-value {"nodeWeight":3.0} to all following row entries in the same level and below (the same scope).

So above f2r would look like this for the engine:

1{
2  "id": "f2r",
3  "posX":  0.37,
4  "posY": -0.98,
5  "posZ":  0.22,
6  "nodeWeight": 3,
7}
This feature is very powerful and dangerous at the same time, as these things can leak all over the place with weird consequences. Use with care.

Negating the effects of scope modifiers is also possible by setting them to empty values:

 1{
 2  "vehicle": {
 3    "nodes": [
 4      ["id", "posX", "posY", "posZ"],
 5      ["f3r", -0.35, -1.56, 0.25],
 6      ["f5r",  0.00, -1.58, 0.24],
 7      {"group":"body"},
 8      ["f4l", -0.37, -0.98, 0.44],
 9      ["f8r", -0.37, -0.98, 0.22],
10      {"group":""},
11      ["f2r",  0.37, -0.98, 0.22],
12      ["f3r",  0.37, -0.98, 0.44],
13      ["f1l", -0.69, -0.62, 0.39],
14      ["f1r", -0.65, -0.64, 0.22]
15    ],
16  }
17}

In above example the group modifier would only apply to node f4l and f8r.

Table row modifiers

For example:

1{
2  "vehicle": {
3    "nodes": [
4      ["id", "posX", "posY", "posZ"],
5      ["f2r",  0.37, -0.98, 0.22, {"nodeWeight": 3}],
6      ["f3r",  0.37, -0.98, 0.44],
7    ],
8  }
9}

Line 5 in above example contains a row with a dictionary behind the columns. These values are applied for the row only and do not leak anywhere else. The node f2r would have the nodeWeight property, the node f3r would not.

This is preferred way of specifying additional modifiers as they do not leak into other parts below and all.

Anatomy of a vehicle jbeam

Vehicles in BeamNG are build together with parts, those parts contain then the so-called sections that contain the actual data.

Every jbeam file is a flat key-value dictionary of available parts this file contains. I.e.

1{
2"autobello_fender_RR": {
3  // ...
4},
5"autobello_fender_RL": {
6  // ...
7},
8}

The key is the name of the part, its value are its sections. The engine constructs a tree of parts during loading time and it needs to know what’s the base or root part. "slotType":"main" is doing that.

Lets try to look at a simple complete jbeam file (to keep it readable we cut it short in some points) (barrier.jbeam):

 1{
 2"barrier_01l": {
 3  "information":{
 4    "name":"Concrete Barrier",
 5    "authors":"BeamNG",
 6  },
 7  "slotType":"main",
 8  "refNodes":[
 9    ["ref:", "back:", "left:", "up:"],
10    ["barrier_11", "barrier_9", "barrier_4", "barrier_15"],
11  ],
12  "cameraExternal":{
13    "distance":5.0,
14    "distanceMin":3,
15    "offset":{"x":0, "y":0.4, "z":0.5},
16    "fov":65,
17  },
18  "flexbodies": [
19    ["mesh", "[group]:"],
20    {"rotation":{"x":0, "y":0, "z":0}, "translation":{"x":0, "y":0, "z":0}},
21    ["barrier_01", ["barrier"]],
22  ],
23  "nodes": [
24    ["id", "posX", "posY", "posZ"],
25    {"nodeWeight":222.222},
26    {"group":"barrier"},
27    {"nodeMaterial":"|NM_ASPHALT"},
28    {"frictionCoef":1.3},
29    {"collision":true},
30    {"selfCollision":false},
31    ["barrier_0", -1.5, -0.4, 0.0],
32    ["barrier_1", -1.5, -0.16, 0.6],
33    ["barrier_2", -1.5, 0.4, 0.0],
34    // ...
35    ["barrier_16", -1.5, -0.16, 1.1],
36    ["barrier_17", -1.5, 0.16, 1.1],
37    {"group":""},
38  ],
39  "beams": [
40    ["id1:", "id2:"],
41    {"beamPrecompression":1, "beamType":"|NORMAL", "beamLongBound":1, "beamShortBound":1},
42    {"beamSpring":10000000,"beamDamp":38000},
43    {"beamDeform":"FLT_MAX","beamStrength":"FLT_MAX"},
44    //main shape
45    ["barrier_5","barrier_15"],
46    ["barrier_2","barrier_3"],
47    // ...
48  ],
49  "triangles": [
50    ["id1:","id2:","id3:"],
51    {"groundModel":"asphalt"},
52    {"group":"barrier"},
53    ["barrier_7","barrier_6","barrier_9"],
54    ["barrier_5","barrier_4","barrier_6"],
55    // ...
56  ],
57},
58}

Lets try to dissect above example:

  • barrier_01l is the name of the part
  • "slotType":"main" defines that this is the root part and can be directly spawned.
  • information, refNodes, cameraExternal, flexbodies, nodes, beams, triangles are sections in the part that contain the data.

Parts and slots

TODO: explain how:

  • the slot system and the hierarchy works
  • explain how the configs work and how parts are being selected
Page created: 20 March 2021, at 22:27
Last modified: 17 April 2021, at 17:12

Any further questions?

Join our discord