Godot 4 opens a Godot 3 TileSet and gives you an empty resource — no importer, no upgrade dialog, no warning. Paste the .tres here and get the Godot 4 file. The atlas geometry carries over exactly; the autotile bitmasks cannot, and this page names every subtile that changes meaning instead of quietly mangling it. Nothing is uploaded.
.tres hereSave it into your Godot 4 project next to the texture the original pointed at, keeping the same res:// path. Then open it once in the editor so the terrain colours show up in the TileMap paint tools.
The Godot 3 TileSet and the Godot 4 TileSet share a class name and nothing else. In Godot 3 a tileset was a flat table of numbered tiles — 0/region, 0/tile_mode, 0/autotile/bitmask_flags — inside a resource marked format=2. In Godot 4 it is a set of TileSetAtlasSource sub-resources with terrain sets and peering bits, marked format=3.
There is no converter in the engine. Godot 4 parses the old file, finds no property it recognises, drops all of them, and hands you a valid, empty TileSet. Nothing errors. That is why the usual answer on the forums is "rebuild it by hand", and why people who bought a tileset tool are hit hardest: Tilesetter's Godot export is Godot 3.x, and TilePipe2 states it supports "plain texture export or Godot 3.x tileset format". Their output is exactly the file Godot 4 throws away.
The geometry is not a guess. A Godot 3 subtile at autotile coordinate c covers region.position + (tile_size + spacing) * c. A Godot 4 atlas tile at coordinate c covers margins + c * (texture_region_size + separation). Those are the same expression, so the mapping is a rename:
| Godot 3 | Godot 4 | |
|---|---|---|
N/region position | margins | same pixels |
N/autotile/tile_size | texture_region_size | same pixels |
N/autotile/spacing | separation | same pixels |
| autotile coordinate | atlas coordinate | unchanged, tile for tile |
tile id N | source id N | unchanged |
N/name | terrain name | one terrain set per Godot 3 autotile |
N/z_index | z_index | same meaning |
N/shapes polygons | physics_layer_0 polygons | re-origined, see below |
Collision moves origin: Godot 3 stores a tile's polygon in pixels measured from the tile's top-left corner; Godot 4 measures from the tile's centre. So every point shifts by shape_transform.origin − tile_size / 2, and nothing else about the polygon changes. A rotated or scaled shape_transform is refused rather than flattened, because a rotation baked into points is a shape you can no longer edit back.
Godot 3 had three bitmask modes. Godot 4 has three terrain modes. They are not the same three.
| Godot 3 bitmask mode | Tiles | Godot 4 terrain mode | Lossless? |
|---|---|---|---|
BITMASK_2X2 | 16 | MATCH_CORNERS | yes |
BITMASK_3X3_MINIMAL | 47 | MATCH_CORNERS_AND_SIDES | yes |
BITMASK_3X3 | 256 | MATCH_CORNERS_AND_SIDES | no |
Godot 4 reads a corner peering bit only when both of the sides beside it are also set. A Godot 3 BITMASK_3X3 tileset is allowed to say "my top-left neighbour is mine, but my top one is not" — 256 combinations — and Godot 4 has no way to write that down. Converting it means putting every mask through the corner rule, which collapses 256 possibilities onto 47.
Two things then go wrong silently, and both are reported here by subtile:
If your file is BITMASK_3X3_MINIMAL — which is what a 47-tile blob sheet uses, and what most tools export — none of this applies and the conversion is exact.
Refusing beats guessing. These are named in the report with the tile they came from, and left out of the file:
| Godot 3 | Why not |
|---|---|
occluder, autotile/occluder_map | Godot 4 keeps light occluders on their own layer with a different shape. Inventing them would put geometry in your scene you never drew. |
navigation, autotile/navpoly_map | Same: Godot 4 navigation layers are not the Godot 3 polygon. |
autotile/priority_map | Godot 4 uses per-alternative probability instead. Set it on the tiles you wanted more often. |
autotile/z_index_map | Per-subtile z-index has no direct equivalent. |
tex_offset | Godot 4's texture_origin is measured from the tile centre with the opposite sign convention. It is printed for you instead of written in with a guessed sign. |
| Circle / capsule / concave collision | Godot 4 tile collision is a polygon. Draw those again in the TileSet editor. |
A .tres is a text file, and it is easy to write one that looks right and loads as an empty resource: a peering-bit key with a typo is not an error in Godot, it is an unknown property the engine drops. So the converter is not tested by reading its own output.
The test suite writes a Godot 3 TileSet, converts it, drops the result into a bare project and runs Godot 4.3 and Godot 4.7 headless over it. A script asks the engine what it understood — get_terrain_set_mode(), TileData.get_terrain_peering_bit(), TileSetAtlasSource.margins, get_collision_polygon_points() — and every answer is compared against the Godot 3 bitmask it came from, subtile by subtile. The bitmask arithmetic itself is taken from the engine's own source: bind bit values and the flat Vector2, int shape of autotile/bitmask_flags are what scene/resources/tile_set.cpp reads and writes.
res:// path or edit the ext_resource line.TileMap nodes are a separate problem — Godot 4.3 deprecated TileMap in favour of TileMapLayer. There is a converter for that too: paste your .tscn, or port the script that drives it.Their Godot export is Godot 3.x, so this page is the missing step. Two things worth knowing after the conversion:
.tres from the image, reads the tile size off the sheet, and tells you which slots are transparent before you find the hole in your map..tres already in it.Turn six hand-drawn tiles into a full 47-tile blob sheet plus the wired Godot 4 .tres, in one pass. Blobsmith on itch.io — or the free Lite version in your browser. The addon that wires a sheet inside the editor is MIT on GitHub.