Page tree
Skip to end of metadata
Go to start of metadata

Packet

TRN.ASWM packets are used to store the walkmesh information in TRN and TRX files.

The packet itself is stored as is:

TypeNameDescription
char[4]data_typeAlways COMP for compressed walkmesh
uint32_tcompressed_data_sizeSize of compressed_data
uint32_tuncomp_lengthUncompressed walkmesh size
void[compressed_data_size]compressed_datazlib-compressed walkmesh data

Uncompressed walkmesh data can be retrieved using zlib.deflate. The following structure information are for the uncompressed walkmesh data.

TRN/TRX differences

TRN.ASWM is slightly different weather it is stored in a TRX or TRN file. Generally, the TRX version is smaller as it has to be downloaded/loaded by the client, and contains the baked version of the walkmesh (with placeable walkmesh/walkmesh cutter alterations).

  • TRN version:
    • The whole map is stored, but no information on placeable walkmeshes nor walkmesh cutters
    • Triangle clockwise flag is not set
    • Contains map border geometry
    • Path tables & island list are empty
  • TRX version:
    • Baked counterpart of the TRN version
    • Map borders are not stored, only the center.
    • Triangles cut by a "walkmesh cutter" are removed (creates holes in the mesh)
    • Placeable walkmesh modifications appears
    • Path tables & island list are populated

Data blocks

TypeNameDescription
Headerheader
Vertex[header.vertices_count]vertices
Edge[header.edges_count]edges
Triangle[header.triangles_count]triangles

TilesHeader

tiles_header

Tile[tiles_header.grid_height * tiles_header.grid_width]

tiles
uint32_ttiles_border_sizeWidth of the map borders in tiles
uint32_tislands_lengthLength of islands
Island[islands_length]islands
IslandPathNode[islands_length * islands_length]islands_path_nodesPathfinding data

Header struct (53 bytes)

TypeNameDescription
uint32_tversion
char[32]name
uint8_towns_data
uint32_tvertices_count
uint32_tedges_count
uint32_ttriangles_count
uint32_ttriangles_offsetSeems to be always 0

Vertex struct (12 bytes)

TypeDescription
float[3]x/y/z coordinates of the vertex

Edge struct (16 bytes)

TypeDescription
uint32_t[2]Vertex indices
uint32_t[2]Attached triangle indices. -1 if a triangle does not exist.
Notes
  • The struct defines a edge between two triangles, with the shared edge.
  • Every triangle has 3 Edge structs.
  • Probably only used to ease path tables generation
  • TODO: I should try remove all Edges from a baked trx and see what happens

Triangle struct (64 bytes)

TypeNameDescription
uint32_t[3]verticesVertex indices composing the triangle
uint32_t[3]linked_edgesEdge indices corresponding to this triangle edges
uint32_t[3]linked_trianglesTriangle indices that have an edge in common with this triangle. -1 is there is no triangle on one.
float[2]centerTriangle center x/y coordinates
float[3]normalNormal vector
floatdotDot product at plane
uint16_tisland-1 if the triangle is non walkable, else it is an island index
uint16_tflagsWalkmesh flags
  • linked_triangles[i] and linked_edges[i] must share the same edge
Triangle flags
  • walkable = 0x01: if the triangle can be walked on
  • clockwise = 0x04: vertices are wound clockwise and not ccw
  • dirt = 0x08: Floor type (for sound effects)
  • grass = 0x10: Floor type (for sound effects)
  • stone = 0x20: Floor type (for sound effects)
  • wood = 0x40: Floor type (for sound effects)
  • carpet = 0x80: Floor type (for sound effects)
  • metal = 0x100: Floor type (for sound effects)
  • swamp = 0x200: Floor type (for sound effects)
  • mud = 0x400: Floor type (for sound effects)
  • leaves = 0x800: Floor type (for sound effects)
  • water = 0x1000: Floor type (for sound effects)
  • puddles = 0x2000: Floor type (for sound effects)
island & walkable flag
  • In TRN files:
    • Map border megatiles triangles have island=-1 and walkable flag set to 0
    • Bakeable triangles (map center) have a valid island (ie: != -1), and the walkable flag set to 1/0 if the triangle is meant to be walkable/non walkable.
  • In TRX files:
    • Map borders are not stored
    • Non walkable triangles have island=-1 and walkable flag set to 0
    • Triangles cut by a "walkmesh cutter" are not stored
Notes
  • For the raw map:
    • unknownA is always 0 (or -0)
  • For the icy peak map
    • -498.294 ==> 475.229

TilesHeader struct (20 bytes)

TypeNameDescription
uint32_tflagsAlways 31 in TRX files, 15 in TRN files
floatwidthWidth in meters of a terrain tile (most likely to be 10.0)
uint32_tgrid_heightNumber of tiles along Y axis
uint32_tgrid_widthNumber of tiles along X axis
uint32_tborder_sizeWidth of the map borders in tiles (8 means that 8 tiles will be removed on each side)

Tile struct (variable length)

A tile is a square (generally 10 x 10 meters) containing mesh triangles. In TRX files, the tiles also contains pre-calculated pathfinding information.

TypeNameDescription
TileHeaderheader
Vertex[]verticesUnused by nwn2. Might be usable with tile.header.owns_data == true
Edge[]edgesUnused by nwn2. Might be usable with tile.header.owns_data == true
PathTableHeaderpath_table_header
ubyte[]local_to_nodelocal_to_node[localTriangleIndex] is an index in nodes
uint32_t[]node_to_localnode_to_local[nodeValue] is a local triangle index
ubyte[]nodesnodes[node_to_local_length * fromLTNIndex + destLTNIndex] is an index in node_to_local
uint32_tflagsReuse value aswm.tiles_header.tiles_flags
  • All triangles of a tile must have consecutive indices
  • The triangle owned by a tile can be retrieved using aswm.triangles[header.triangles_offset .. header.triangles_offset + header.triangles_count]
  • A tile must not re-use triangles owned by another tile (will crash NWN2)
  • TODO: I need to try making non-square tiles and see how the game handles it

Tile path table

This is a pre-calculated pathfinding table, that reference the next triangle to go to in order to reach any other triangle of the tile.

This is how a path on a tile is calculated:

  1. Get local triangle indices:
    • fromLocIdx = fromGlobIdx - tile.header.triangles_offset for the starting triangle
    • destLocIdx = destGlobIdx - tile.header.triangles_offset for the triangle you are trying to reach
    • If fromLocIdx or destLocIdx equals 255, it means the triangle is not walkable
  2. Get node indices:
    • fromNodeIdx = tile.local_to_node[fromLocIdx]
    • destNodeIdx = tile.local_to_node[destLocIdx]
  3. Get the Node value
    • node = tile.nodes[fromNodeIdx * header.node_to_local_length + destNodeIdx]
    • node == 255 can mean two things:
      • Destination cannot be reached, because one of the triangles is non- walkable or there is an obstacle that can't be get around without leaving the tile.
      • If fromNodeIdx == destNodeIdx, this means you already reached the destination triangle
  4. Get the next local triangle index to go to in order to reach destination
    • nextLocIdx = tile.node_to_local[node & 0b0111_1111]
  5. Loop on 2. with fromNodeIdx = nextLocIdx, until nextLocIdx == destNodeIdx

Notes:
  • nodes[i] & 0b1000_0000 is a bit flag for if there is a clear line of sight between the two triangle. It's not clear what LOS is since two linked triangles on flat ground may not have LOS = 1 in game files. A value of 0 means the game will have to calculate LOS in real time.

TileHeader struct (57 bytes)

TypeNameDescription
char[32]nameSometime contains garbage, sometime contains only null values
ubyteowns_data1 if the tile stores vertices / edges. NWN2 always set this to 0
uint32_tvertices_countNumber of vertices in this tile
uint32_tedges_countNumber of edges in this tile
uint32_ttriangles_countNumber of triangles in this tile (walkable + unwalkable)
floatsize_xTODO: Always 0?
floatsize_yTODO: Always 0?
uint32_ttriangles_offset

PathTableHeader struct (13 bytes)

TypeNameDescription
uint32_tcompression_flagsAlways 0. Note: it's pointless to compress path table since the aswm is already compressed
uint32_tlocal_to_node_lengthLength of local_to_node array
ubytenode_to_local_lengthLength of node_to_local array
uint32_trle_table_sizeAlways 0.
  • compression_flags values: rle = 1, zcompress = 2

Island struct (variable length)

An island is a fraction of a tile, where all walkable triangles can be reached without leaving the tile. This is a pathfinding related struct that is not available in TRN files.

TypeNameDescription
IslandHeaderheader
uint32_tlinked_islands_lengthLength of linked_islands
uint32_t[]linked_islandsIsland indices linked to this one
uint32_tlinked_islands_dist_lengthLength of linked_islands_dist
float[]linked_islands_distDistance to linked islands
uint32_tlinked_islands_exit_lengthLength of linked_islands_exit
uint32_t[]linked_islands_exitExit triangles leading to linked islands
  • linked_islands_dist[i] and linked_islands_exit[i] are the distance & exit to linked_islands[i]
  • I don't see the point of storing 3 times the same length. Maybe the nwn2 devs did not have time clean this.

IslandHeader struct (24 bytes)

TypeNameDescription
uint32_tindexIndex of the island in the aswm.islands array
uint32_ttileValue looks pretty random, but is identical for all islands. TODO: try setting it to associated tile index
float[3]centerCenter of the island. Z is always 0
uint32_ttriangles_countNumber of triangles in this island

IslandPathNode struct (8 bytes)

Pathfinding across islands works very similarly to Tile path table.

aswm.islands_path_nodes[fromIslandIdx * aswm.islands_length + toIslandIdx] is the next island to go to in order to reach toIslandIdx from fromIslandIdx

TypeNameDescription
uint16_tnextNext island to go to
uint16_t_paddingunused
floatweightDistance to the next island
  • No labels