WoWs Model Exported
World of Warships 3D model Exporter
Loading...
Searching...
No Matches
WoWs ship 3D model representation

How World of Warships assembles a ship mesh for export: param lookup, part files, scene graph (assets.bin), materials, and textures. Raw .geometry byte layout is documented in GEOMETRY.md.

Contents

  • Overall Layout
  • Draw-call mapping IDs
  • Ship parts and LOD
  • Coordinate system
  • GameParams.data
  • assets.bin
  • Texture files

Overvall Layout

Here is how the game files fit together to construct a ship model:

flowchart TB
subgraph GP["GameParams.data"]
ship["ship param e.g. PJSB007"]
upgrade["ShipUpgradeInfo → hull upgrade"]
hullModel["HullComp.model path"]
mountsGP["ArtComp HP_* → mount .model"]
end
subgraph GEOM[".geometry per ship part"]
geomFiles["ShipName.geometry, _Bow, _MidFront, …"]
vbloc["vertex_bloc_map (vertex draw-call table)"]
ibloc["index_bloc_map (index draw-call table)"]
encd["ENCD merged vertex / index buffers"]
end
subgraph AB["assets.bin"]
visual["VisualPrototype<br/>path lookup on .visual suffix"]
rs["render_sets[]"]
lods["lods[]"]
hpNodes["nodes[] + matrices[]<br/>HP_* transforms"]
end
subgraph TEX["textures"]
mfm[".mfm beside material path"]
dds["stem_a / stem_n / stem_mg<br/>.dd0 or .dds"]
end
subgraph OUT["GLB export"]
hullOut["hull: index blocs → primitives<br/>filter LOD + damage"]
mountOut["mounts: .geometry @ HP_world × ROT180Y"]
end
ship --> upgrade
upgrade --> hullModel
hullModel -->|"swap .model → .geometry"| geomFiles
geomFiles --> vbloc
geomFiles --> ibloc
geomFiles --> encd
vbloc <-->|"packed_texel_density"| ibloc
geomFiles <-->|"same path, .visual suffix"| visual
visual --> rs
visual --> lods
visual --> hpNodes
rs -->|"indices_mapping_id"| ibloc
rs -->|"vertices_mapping_id"| vbloc
rs --> mfm
mfm --> dds
lods -->|"active mapping_id set"| ibloc
mountsGP -->|"mount .geometry"| mountOut
hpNodes --> mountOut
encd --> hullOut
ibloc --> hullOut
rs --> hullOut
lods --> hullOut
dds --> hullOut

Draw-call mapping IDs

flowchart LR
subgraph geom[".geometry"]
ibloc["index_bloc_map[i]<br/>mapping_id"]
vbloc["vertex_bloc_map[j]<br/>mapping_id"]
end
subgraph abin["assets.bin RenderSet"]
idx["indices_mapping_id"]
vtx["vertices_mapping_id"]
mfm["mfm_path"]
names["name_id / node names"]
end
ibloc <-->|"same uint32"| idx
vbloc <-->|"on-disk id"| vtx
vbloc <-.->|"decode: packed_texel_density"| ibloc
idx --> lod["LOD filter"]
idx --> tex["albedo / materials"]
idx --> dmg["damage exclusion"]
mfm --> dds["stem_a / stem_n / stem_mg textures"]
names --> dmg

See draw-call matching for how vertex and index blocs are paired inside a .geometry file.

Ship parts and LOD

File naming

A ship's geometry is split across multiple .geometry files in the same directory:

content/gameplay/japan/ship/battleship/JSB007_Kongo_1942/
JSB007_Kongo_1942.geometry ← low-poly LOD placeholder (whole-ship bounding mesh)
JSB007_Kongo_1942_Bow.geometry ← detailed bow section
JSB007_Kongo_1942_MidFront.geometry ← detailed mid-front section
JSB007_Kongo_1942_MidBack.geometry ← detailed mid-back section
JSB007_Kongo_1942_Stern.geometry ← detailed stern section
...

The bare base file (same name as the directory) is a low-detail stand-in used at extreme camera distances. The suffixed part files (_Bow, _MidFront, etc.) hold the actual high-detail mesh. All parts share the same ship coordinate space and can be merged directly without any transform.

A corresponding .visual file exists for each .geometry file at the same path. The .visual path is the key used to look up the VisualPrototype record in assets.bin.

Render-set LOD (in <tt>assets.bin</tt>)

Within a single part file, multiple detail levels are expressed as groups of render sets in the VisualPrototype record (see VisualPrototype). Each LOD entry lists which render sets (identified by indices_mapping_id) belong to that detail level:

LOD level Description
0 Highest detail (full geometry + normal maps)
1 Medium detail
2 Low detail
3 Lowest detail / impostor

A render set's indices_mapping_id matches the mapping_id field in the index bloc mapping table of the .geometry file. This is how LOD filtering maps directly onto primitive selection during export.

Damage and cross-section geometry

Ships split into sections on sinking. Each break point has associated geometry:

Node name pattern Role
Xxx_crack_YYY_DeckHouse Exterior deckhouse face at break joint (visible at all times)
Xxx_crack_YYY_Hull Exterior hull face at break joint (visible at all times)
Xxx_crack_YYY_in Inner cross-section face revealed when the ship splits (damage)
Xxx_crack_YYY Inner/cross-section face (bare name, no suffix) (damage)
Xxx_hide Hidden torn-metal mesh revealed on break (damage)

Node names are stored in the VisualPrototype and resolved via the strings section of assets.bin. Render sets associated with _hide or _crack_* nodes (except _DeckHouse variants) are considered damage geometry.

Damage materials also identify damage primitives:

  • *Razlom* — torn-metal texture used at break surfaces
  • *C011_Grid* — grid/alpha overlay used at break points

Coordinate system

  • Y-up, right-hand coordinate system (BigWorld convention).
  • Ship faces bow toward −Z.
  • Turret/gun models face stern (+Z) in their local space; a 180° Y-axis rotation is required to align them with the hull when placing at HP_ hardpoints.
  • HP_ hardpoint transforms in assets.bin are world-space, column-major 4×4 float32 matrices.

GameParams.data

Format: reverse(zlib(pickle(root_dict)))

The bytes of the file are stored in reverse order; reversing and zlib-decompressing yields a Python pickle. The root object is either a dict with an "" key (older builds) or a list whose first element is the param dict (newer builds).

Each ship entry is keyed by its param name (e.g. PJSB007, PASC013). Ship entries have typeinfo.type == "Ship" and contain:

Field Description
ShipUpgradeInfo Dict of hull/weapon upgrade configs
<HullCompName> Hull component dict: model path, visibilityFactor, armor, etc.
<ArtilleryComp> Artillery component dict: HP_* mount points with model paths
<TorpedoComp> Torpedo component dict: HP_* mount points

The model field inside a hull component is a path like:

content/gameplay/japan/ship/battleship/JSB007_Kongo_1942/JSB007_Kongo_1942.model

Replace the .model suffix with .geometry to obtain the geometry file path relative to the game root directory.

Armor key encoding

The armor dict inside a hull component uses integer keys:

key = (model_index << 16) | material_id
value = thickness in mm (float)
  • model_index — zero-based index into the armor model blocs in the .geometry file.
  • material_id — armor surface material (determines penetration/bounce behaviour).

assets.bin (BigWorld PrototypeDatabase)

assets.bin is the BigWorld PrototypeDatabase binary format. It stores pre-compiled scene-graph data (visual prototypes) for all game objects.

File header (16 bytes)

Offset Size Field Value / Notes
0 4 magic 0x42574442 ("BWDB" LE)
4 4 version 0x01010000
8 4 checksum CRC of the body
12 2 architecture
14 2 endianness

Body starts at offset 0x10.

Body layout

Base offset Size Section
0x10 0x28 Strings section
0x38 0x18 R2P (record-to-path) map
0x50 0x10 Path storage
0x60 Databases array

All sections use relative pointers (int64_t, signed, from the field's own address).

Strings section (base = <tt>0x10</tt>)

Open-addressing hashmap mapping uint32_t name IDs to null-terminated UTF-8 strings.

Offset in section Field Description
0 capacity (u32) Number of buckets
4 pad
8 buckets_relptr → array of [u32 key, u32 sentinel] pairs
16 values_relptr → array of u32 byte offsets into string data
24 string_data_size Size of string data blob in bytes
28 pad
32 string_data_relptr → null-terminated strings

Lookup: probe (key % capacity + i) % capacity until key matches or bucket is empty (both fields zero).

R2P map (base = <tt>0x38</tt>)

Maps path self_id (uint64_t) to a packed uint32_t value encoding the blob and record where the prototype data lives.

Offset in section Field Description
0 capacity (u32) Number of buckets
4 pad
8 buckets_relptr → array of [u64 key, u64 ] pairs (16 B each)
16 values_relptr → array of u32 packed values

Packed value decoding:

blob_index = (value & 0xFF) // 4
record_index = value >> 8

Path storage (base = <tt>0x50</tt>)

Flat array of path entries (32 bytes each):

Offset in entry Field Description
0 self_id uint64_t unique ID for this path node
8 parent_id uint64_t ID of the parent node (0 = root)
16 packed string Node name (BigWorld packed string: u32 char_count, 4 B pad, i64 text_relptr)

Full paths are reconstructed by walking parent links. A .visual path suffix (e.g. JSB007_Kongo_1942/JSB007_Kongo_1942.visual) is the lookup key.

Databases array (base = <tt>0x60</tt> in body; relptr base = body base)

Array of database entries (0x18 bytes each):

Offset in entry Field Description
0 proto_magic Prototype type magic (u32)
4 proto_checksum (u32)
8 size Byte size of the blob (u32)
12 pad
16 data_relptr i64 relative to entry base → blob

Each blob starts with a uint64_t record count, followed by fixed-size records. Blob index 1 holds VisualPrototype records (item size = 0x70).

VisualPrototype

Record format (item size 0x70 = 112 bytes):

Offset Size Field Description
0 4 nodes_count Number of scene-graph nodes
4 4 pad
8 8 name_map_name_ids_rp i64 relptr → u32[] name IDs (length = nodes_count)
16 8 name_map_node_ids_rp i64 relptr → u16[] node indices
24 8 name_ids_rp i64 relptr → u32[] name IDs (one per node)
32 8 matrices_rp i64 relptr → 16×f32 column-major matrices (64 B each)
40 8 parent_ids_rp i64 relptr → u16[] parent indices (0xFFFF = root)
48 8 merged_geom_path_id u64 path ID of the merged geometry resource
56 1 underwater_model u8
57 1 abovewater_model u8
58 2 render_sets_count u16
60 1 lods_count u8
61 3 pad
64 32 BoundingBox min + max as 2 × vec3f32 + 2 × 4 B pad
96 8 render_sets_rp i64 relptr → RenderSet array
104 8 lods_rp i64 relptr → LOD entry array

World-space transform for a node = product of local matrices from root to node (column-major, right-to-left multiplication).

RenderSet (0x28 = 40 bytes each)

Offset Size Field Description
0 4 name_id u32 name string ID (render set name)
4 4 mat_name_id u32 material name string ID
8 4 vertices_mapping_id u32 matches mapping_id in vertex bloc mapping table (GEOMETRY.md)
12 4 indices_mapping_id u32 matches mapping_id in index bloc mapping table
16 8 mfm_path_id u64 path ID of the .mfm material file
24 1 skinned u8
25 1 nodes_count u8 number of bone nodes
26 6 pad
32 8 node_name_ids_rp i64 relptr → u32[] node name IDs

LOD entry (16 bytes each)

Offset Size Field Description
0 4 dist_threshold f32 camera distance threshold for this LOD level
4 2 unknown u16
6 2 rs_count u16 number of render sets in this LOD
8 8 rs_names_rp i64 relptr from this entry's baseu32[] name IDs

Name IDs reference render sets by their name_id field. Resolved to indices_mapping_id for LOD-based primitive filtering.

Texture files

Textures live in the same directory as the .mfm file they are referenced from. The stem is derived from the MFM filename (minus the .mfm extension and optional MFM-only suffixes: _skinned, _alpha, _ep).

Suffix Channel glTF slot
_a Albedo / base color baseColorTexture
_n Normal map normalTexture
_mg Metallic/gloss metallicRoughnessTexture

File extensions in priority order: .dd0 (highest resolution MIP set), .dd1, .dds. All are standard DDS containers.

UV orientation: BigWorld stores V with origin at the bottom; DDS textures are stored top-to-bottom. Correct display requires flipping V: v_display = 1.0 - v_stored. In glTF this is expressed with KHR_texture_transform: scale=[1,-1], offset=[0,1].