Skip to content

Map Tile Format

Leonhard S edited this page Oct 6, 2021 · 3 revisions

The following section covers the file format and naming convention used for in-game map tiles.

Related links:

  • Map Tile Extractor  --  Utility for exporting map tile assets from a locally installed PS2 install.
  • PS2 Map Viewer  --  A TypeScript implementation for client-side handling of PS2 map tiles.
  • Merged Map Assets  --  All map tiles for a given map and LOD merged into a single PNG.

Level of Detail (LOD)

The maps in PlanetSide 2 come in multiple resolutions depending on the zoom level. This is referred to as the map asset's "level of detail" or LOD.

Every map has four levels of detail, regardless of the physical size of the map area. These are numbered 0 through 3, with LOD 0 being the highest resolution available and each subsequent LOD being a halving of resolution.

For Indar's map, this means that the total resolution of all tiles for LOD 0 is 8192px, 4096px for LOD 1, 2048px for LOD 2, and 1024px for LOD 3.

Map Sizes

At their base size (i.e. LOD 0), one pixel on the map corresponds to 1 metre in the game.

The primary continents (Indar, Hossin, Amerish, and Esamir) therefore have LOD 0 maps of 8192 x 8192 pixels, which results in the 8 by 8 playable kilometres in-game (ignoring the out-of-bounds areas at the edge).

Other zones like Koltyr or VR training are physically smaller, and in return their LOD 0 maps are also of a lower resolution.

The following table lists all currently available zones, along with the texture size for each LOD:

Zone LOD 0 LOD 1 LOD 2 LOD 3
Amerish 8192 4096 2048 1024
Esamir 8192 4096 2048 1024
Hossin 8192 4096 2048 1024
Indar 8192 4096 2048 1024
Nexus 4096 2048 1024 512
OutfitWars
(Desolation)
8192 4096 2048 1024
quickload
(Koltyr)
4096 2048 1024 512
Sanctuary 1024 512 256 256*
Tutorial
(Old Tutorial)
1024 512 256 256*
VR
(VR Training)
2048 1024 512 256
VRTutorial2
(New Tutorial)
2048 1024 512 256

* The in-game tiles' size is 256 pixels, regardless of map resolution. Since the nominal tile size for these small maps is less than the actual map tiles, the tile contents only take up 1/4 of the image.

Tile Asset Naming Scheme

The game tiles are named according to the following pattern:

<zone_code>_Tile_<coordX>_<coordY>_LOD<level_of_detail>.dds
  • zone_id  The internal identifier for the zone. Usually this is just the zone name, but some use special identifiers or development names (e.g. "quickload" for Koltyr).
  • coordX, coordY  Tile coordinates for this tile. Note that these are always padded to 3 characters length, with the first character being either a leading 0 (for positive values) or a minus sign.
  • level_of_detail  The LOD of the tile asset, 0 through 3.

Coordinate Grid

Map tiles are arranged in their own indexed coordinate grid.

This section outlines the basic building blocks required to navigate this grid for all map sizes and LODs.

Grid Step Size

The step size (i.e. the distance between two grid positios) primarily depends on the current LOD value, with an increment in the LOD resulting in a doubling of step size as the size of each tile also doubled.

Small maps (2048px base size and below) will additionally clamp the step size at a certain value, regardless of the level of detail.

Long story short, the following function has you covered:

def map_step_size(map_size: int, lod: int) -> int:
    if lod == 0:
        return 4
    if lod == 1 or map_size <= 1024:
        return 8
    if lod == 2 or map_size <= 2048:
        return 16
    return 32

Tile Count

A helper function for the number of tiles required to display a given map and LOD.

def map_tile_count(map_size: int, lod: int) -> int:
    return math.ceil(4 ** (math.floor(math.log2(map_size)) - 8 - lod))

Grid Limits

The minimum and maximum coordinate of the grid, along with the step size, are required to iterate over its indices.

The grid is biased towards the origin, with index 0 already being part of the positive half of the coordintae range.

def map_grid_limits(map_size: int, lod: int) -> Tuple[int, int]:
    step_size: int = map_step_size(map_size, lod)
    tiles_per_axis: int = math.floor(math.sqrt(map_tile_count(map_size, lod)))
    half_size: int = step_size * tiles_per_axis // 2
    if half_size <= 0:
        return -step_size, -step_size
    return -half_size, half_size - step_size

Example Implementation

This format documentation was compiled while writing my own tile rendering engine in TypeScript.

This component handles the generic tile-rendering side of things (placing them on the canvas, zooming, etc.):
map-engine/tile-layer.ts

The parts specific to how PlanetSide 2 handles its terrain tiles reside in their own file:
layers/terrain-layer.ts