Skip to content

Nodes

A map's content is a tree of nodes. Every node has the same outer shape, with its keys in this order:

Key Type Written Meaning
id integer always Unique in the map
type string always layer, group, entity, brush, mesh, terrain, scatter or instance
type fields The keys of that node type, see below, Geometry and Terrain and scatter
label string when set Name given with Rename, see below
graph array of 2 numbers when set Where an entity's node sits in the Logic graph
hidden bool when true Hidden in the editor
locked bool when true Cannot be selected or edited in the editor
children array of nodes when not empty Child nodes, in order

hidden and locked are editor state only, so Godot still builds a hidden node. To keep something out of the build, put it in a layer with omit_from_export.

label is the name you gave a brush, mesh, entity, instance or terrain in the editor, shown instead of a default name like brush12. Layers, groups and scatter sets never have one, they keep theirs in name.

Godot only uses label for terrains, it names the node terrain_<id>_<label>. Brushes and meshes merge into their entity, and an entity takes its node name from targetname. Editors older than the key still read files with it, but drop it when they save.

graph is editor state too. It's where an entity's node sits in the Logic graph, in graph points, and only nodes someone moved have it. Godot ignores it, and a value that isn't two numbers is dropped on load.

Ids

Ids are positive integers that stay the same across saves, so other data can point at a node. The live link sends edits by id, scatter sets name their surfaces by id, and Godot stores each generated node's id in its _td_id metadata. An id of 0 or a duplicate, which only a hand edited file has, gets a fresh id on load.

A prefab is a separate .tdm file with ids of its own, so two copies of it would clash. To avoid that, the Godot build adds a million times a per occurrence number to the id of every node an instance brings in. Two copies of one prefab never share ids, however deep they're nested.

Where nodes go

Node Allowed parent
Layer Top level only
Group Layer or group
Brush, mesh, terrain, scatter set, point entity, instance Layer or group
Brush or mesh of a brush entity That entity

An entity with children is a brush entity, a door made of brushes for example. Brushes and meshes outside any entity belong to worldspawn.

Layer

A top level part of the map, shown in the editor's Outliner.

Key Type Default Meaning
name string required Name shown in the Outliner
color string required #rrggbbaa. #rrggbb is accepted when reading
omit_from_export bool false Always written. When true, the build skips the whole layer, for reference geometry that should never reach Godot

Group

Nodes gathered so you select and move them together.

Key Type Default Meaning
name string required Name shown in the Outliner
link_id integer omitted Groups sharing a link id are linked copies, made with Duplicate Linked
transform 16 numbers identity, omitted This copy's placement relative to the others, a 4x4 matrix in column major order

Group contents are stored in world space, already in place. A linked copy repeats an edit made in one copy in all the others, and transform is just what the editor uses to carry the edit from one copy to the next. The Godot build ignores both transform and link_id.

Entity

Key Type Default Meaning
classname string required Entity class from the game config, for example prop_physics
origin [x, y, z] [0, 0, 0] Always written. Position of a point entity
angles [pitch, yaw, roll] [0, 0, 0] Always written. Degrees, YXZ order like rotation_degrees
properties object of string to string omitted Entity keys, sorted by name
outputs array omitted I/O connections, see below

Property values are always strings, whatever their FGD type, for example "travel": "0 168 0".

An angles, angle or mangle property wins over the node's angles and is read the Quake way: pitch and roll negated, yaw turned by 180 degrees. On classes starting with light, mangle is yaw, pitch and roll instead. An angle of -1 or -2 points straight up or down. The editor draws the entity facing the same way, and turning it there moves the property into the node's angles.

The build ignores origin and angles of a brush entity, since its brushes already sit where they belong.

Each output is one I/O connection: when this entity fires a signal, call a method on a target.

Key Type Default Meaning
output string required Signal on this entity that triggers the connection
target string required Who receives the call, see Targets
input string required Method to call on the target
parameter string "", omitted Value passed to the input
delay number 0, omitted Seconds to wait before the call
times integer -1, omitted How many times it may fire, -1 for every time

Instance

A reference to another .tdm file, placed with a position and rotation, like Hammer's func_instance. This is how prefabs get placed: the content lives once in its own file, and every instance shows it.

Key Type Default Meaning
path string required The referenced map, relative to the referencing map, absolute, or res://
origin [x, y, z] required Where the instance is placed
angles [pitch, yaw, roll] required Degrees, YXZ order
fixup string "", omitted Name prefix for the instance's contents, see below

The build takes the children of every layer in the referenced map that isn't omitted from export, and moves them by origin and angles. Texture projections move along with the geometry. The referenced map's worldspawn isn't used. Instances nest up to eight levels deep.

A Godot build reads each referenced map once, and all the instances of a map together may add 2,097,152 values, a value being an array item or dictionary entry of the referenced map, with every 8 characters of text counting as one more. Past that the build leaves the remaining instances out and reports an error. Ordinary maps stay far below it, only instances of instances multiplied many times over reach it.

With a fixup of p1, door becomes p1-door in targetname, target, destination, call_target, every property the FGD declares as target_source or target_destination, and every output target. Values starting with !, @ or / are left alone. Nested fixups add up from the outside in, so b inside a gives a-b-door, and Explode Instance in the editor gives the same names.