Skip to content

MeshSets and vertex declarations

Frostbite (skate.)GeometryApplies toskate.

A MeshSet resource (0x49b156d4) describes LODs and their material sections. Each LOD names a geometry chunk holding vertex buffers followed by the index buffer. Stored pointers are relative to byte 16 of the resource. A census reads all 11,619 MeshSets found across the base-game TOCs without parse failures. Proven · all 11,619 MeshSets

The header holds u64 LOD pointers starting at 0x30 and a u16 LOD count at 0xb4. LODs are located through those pointers; their allocation stride is not needed to locate the records. Proven · MeshSet census

A MeshSet is one relocatable block: fixed-size records whose pointers name a pool after them, then a relocation table, then trailing data that only one development mesh has. Its order is the header, the LOD records, every LOD’s section records, then the pool. Every one of the 47,758 MeshSets of the base data, and its bundle resource metadata, is written back byte for byte from this description. Proven · all 47,758 MeshSets

MeshSet headervariable68% known

0xd0 bytes, or 0xe0 when the mesh carries bone bounds. Pointers are relative to byte 16.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32header bytes minus 16Proven
0x44u32LOD record bytesProven0xb0, 0xb8 with bone masks, 0xbc with a bone map
0x84u32 = 384section record bytesProven
0xC4bytesunknownUnknown
0x1016f32[4]bounds minimumObservedOver every section's bounds.
0x2016f32[4]bounds maximumObserved
0x3056u64[7]LOD record pointersProvenAt most seven LODs.
0x688u64name pointerProven
0x708u64pointer to the name's last partProven
0x784u32name hashObserveddjb2-XOR of the MeshSet name; equals the mesh asset's NameHash.
0x7C1u8mesh typeObserved0 rigid (46,977 MeshSets), 1 skinned (296), 2 composite (485); the names are Frostbite's mesh types
0x7D1u8unknownUnknown3 on 47,666 MeshSets, 5 on 92
0xA84u32layout flagsObservedbit 0: geometry in chunks (clear only on the MeshSet with inline geometry); bit 2: some LOD sets its bit 4; bits 1, 4, 7, 8 to 23 and 29 to 31 take other values that are not named here
0xB42u16LOD countProven
0xC42u16bone countProven0xe0 header only.
0xC62u16bounded bone countProven0xe0 header only.
0xC88u64bounded bone ids pointerProven0xe0 header only; u16 each
0xD08u64bone bounds pointerProven0xe0 header only; f32x4 minimum and maximum per bounded bone, else per bone

The pool holds, in order: the bone lists (then 16-aligned); the strings, per LOD its section names then its Mesh:<name>_lodN name, then the MeshSet’s name (16-aligned); each LOD’s five section-index lists (16-aligned); each LOD’s bone masks (16-aligned) or bone map (4-aligned); the bone ids and bone bounds (each 16-aligned). The relocation table lists every pointer field as a u32 of its offset minus 16, in record order. The bundle’s resource metadata is {table offset, table bytes, trailing bytes, 0}. Proven · all 47,758 MeshSets

Exactly one stock MeshSet has trailing data (2,001,568 bytes, a development character mesh), and its LOD 0 is the only one of the 126,014 LODs whose geometry chunk GUID is all zero. That LOD’s vertex buffer (1,733,616 bytes) and index buffer (267,948 bytes) add up to 2,001,564 bytes, four less than the trailing data, so the trailing data is that LOD’s geometry stored inside the resource instead of in a chunk. Proven · all 47,758 MeshSets for the counts The trailing data is the vertex buffer, then the index buffer, then four zero bytes: read as the chunk is read, all 133,974 16-bit indices name one of the section’s 24,078 vertices (the largest is 24,077, and 24,078 × 72 bytes is the vertex buffer), and the last four bytes are zero. Observed · the one MeshSet with trailing data

MeshSet LOD fieldsvariable79% known

Little-endian; offsets are relative to the pointed-to LOD record.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x84u32section countProven
0xC8u64section array pointerProven
0x1460{u32, u64}[5]section-index listsProvenCount and pointer of five byte lists, at 12-byte steps
0x504u32layout flagsObservedbit 6: the geometry is a chunk (clear only on the LOD with inline geometry); bit 4 only on LODs of MeshSets that set header bit 2; bits 0, 9, 17 and 18 take other values that are not named here
0x544u32index formatProven0x21 is 16-bit; 0x2e is 32-bit
0x584u32index buffer byte countProven
0x5C4u32vertex buffer byte countProvenMultiple of 16 in every stock MeshSet; the game also loads community MeshSets that are not (65,400)
0x7416GUIDgeometry chunkProven
0x8C8u64name pointerProven
0x948u64pointer to a part of the nameProven
0x9C8u64pointer to the name's last partProven
0xA44u32path-part name hashObserveddjb2-XOR of <name>_lod0
0xB0variablebytesbone masks or bone mapProven0xb8 records: pointer to a 24-byte bone mask per section; 0xbc records: u32 count, then pointer to u32 bone map

LOD records are 16-aligned, so a 0xb8- or 0xbc-byte record occupies 192 bytes. Proven · all 47,758 MeshSets

In the geometry chunk the index data starts at the vertex buffer byte count; indices are relative to their section. Proven · MeshSet census

The mesh type decides the form of the LOD records and the header: a rigid MeshSet has 0xb0-byte LOD records and the 0xd0-byte header, a skinned one 0xbc-byte records (a bone map) and the 0xe0-byte header with bone bounds, a composite one 0xb8-byte records (a bone mask per section) and the 0xe0-byte header; every section of the 781 non-rigid MeshSets declares blend indices and no section of a rigid one does. Proven · all 47,758 MeshSets Header bit 0 and LOD bit 6 are clear exactly on the one MeshSet, and its one LOD, whose geometry is stored inside the resource. Header bit 2 is set on exactly the 321 MeshSets with a LOD that sets LOD bit 4. Proven[all 47,758 MeshSets and 126,014 LODs] The other bits of both words take several values (a block of four bits at each of header bits 8 to 11, 12 to 15, 16 to 19 and 20 to 23, whose counts rise within the first block: 31,976, 32,149, 33,300, 37,009 MeshSets) and their meaning is not known; LOD bit 0 is set on 50,130 LODs and is not the first LOD of its MeshSet. Observed[all 47,758 MeshSets]

The five lists name the sections each pass draws: list 0 the main pass, list 2 decals, lists 3 and 4 the depth and shadow passes. Inferred This is the order of the subset categories Frostbite meshes use in other games (opaque, transparent, transparent decal, depth-only, shadow). Inferred

The lists are used unevenly. Of the 126,014 LODs of the 47,758 stock MeshSets, list 0 is non-empty in 123,529, list 1 in 228, list 2 in 9,054, list 3 in 123,137 and list 4 in 123,285. The 300,875 sections fall into 23 combinations of list membership and section flags; the largest are below. Proven · all 47,758 MeshSets

Lists holding the section Section flags Sections
0 0x0a 156,048
3 and 4 0x04 113,763
0, 3 and 4 0x0e 10,929
2 0x0a 9,943
0 and 3 0x0a 4,242
4 0x04 3,134
3 0x00 1,268
3 and 4 0x0c 532

The flags byte takes 15 values (0x00, 0x01, 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x1a, 0x1b, 0x1e, 0x1f). 0x0a marks both the sections that only list 0 holds (156,048) and the decal sections that only list 2 holds (9,943), so the byte does not separate an opaque section from a decal. Proven · all 47,758 MeshSets Where a LOD carries shadow-proxy twins of its opaque sections, its primitive counts summed over all sections are twice the triangles it draws in the main pass. Observed

MeshSet section fields384 bytes79% known

Little-endian; undescribed ranges are not interpreted here.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x88u64material name pointerProven
0x108u64bone list pointerProven
0x182u16bone countProven
0x1B1u8flagsObserved15 values below 0x20; 0x0a (170,233 sections), 0x04 (116,897) and 0x0e (10,935) are the commonest; the byte does not say which section list holds the section (0x0a is on sections of list 0 alone and of list 2 alone)
0x1C1u8material slot indexObservedIndex in the mesh asset's Materials.
0x1E1u8total vertex strideProven
0x1F1u8primitive typeObserved3 (triangle list) in all 300,875 sections
0x204u32primitive countProven
0x244u32start indexProven
0x284u32vertex byte offsetProven
0x2C4u32vertex countProven
0x544u32repeated vertex byte offsetProven
0x584f32UV0 texel densityObservedMedian over the triangles of sqrt(UV area / area); 1.0 when no triangle has area
0x5C4f32UV1 texel densityObserved
0x70100declarationstored vertex declarationProven
0xD4100declarationdraw vertex declarationProven
0x1388u64material keyProven
0x1404u32repeated start indexProven
0x1444u32material name hashObserveddjb2-XOR of the lowercased material name.
0x14C4f32surface areaObservedSquare metres.
0x15016f32[4]bounds minimumObservedw is 0
0x16016f32[4]bounds maximumObserved

The surface area matches the area of the decoded triangles on the 1,222 rigid Quick Drop sections with UV0 (1st–99th percentile of stored/measured 0.996–1.003), and the UV0 density matches the median per-triangle sqrt(UV area / area) on the same sections (25th–75th percentile 0.996–1.001). The density does not fit sections whose UVs fold many triangles to nothing. Observed

Vertex declaration100 bytes98% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x064Element[16]element slotsProvenEach element is four bytes in usage/format/offset/stream order.
0x4032u8[16][2]stream descriptorsProvenFirst byte of each pair is stride throughout the MeshSet census; second byte's meaning is not established here.
0x601u8element countProven
0x611u8stream countProven
0x622bytesunknownUnknown

Stored streams occupy consecutive stride * vertex_count ranges from a section’s vertex offset. The draw declaration can reorder elements, supply an unbound element on stream 255, alias a stored stream or append runtime streams; 830 measured skinned sections use the latter. Treating the two declarations as interchangeable loses this distinction. Proven · MeshSet census

Usage Meaning Common format Confidence
1 Position Float3 0x03 or Half4 0x08 Observed
2 Blend indices UShort4 0x17 Observed
4 Blend weights UByte4N 0x0d Observed
30, 31 Color0, Color1 UByte4N Observed
33, 34 UV0, UV1 Half2 0x06 Observed
51 Submaterial selector UByte4 0x0c Observed
52 Packed tangent space UByte4N Observed

Usage 52 stores a vertex’s tangent frame in four bytes (UByte4N). Observed

Bits Meaning Confidence
Byte 3, bits 0–2 Largest normal axis and sign: code 0–5 is +x, −x, +y, −y, +z, −z Observed
Byte 3, bit 3 Bitangent sign, set for −1 Observed
Byte 3, bits 4–5 Ninth bits of the two other normal components Observed
Byte 3, bits 6–7 Quadrant of the tangent angle Observed
Bytes 0, 1 Low eight bits of the two other normal components (axes z,y for codes 0–1; x,z for 2–3; x,y for 4–5); a 9-bit value q decodes as (q-256)/(255*sqrt 2) Observed
Byte 2 Angle within the quadrant, 0–255 for 0–90° Observed

The largest normal component is the remainder of unit length. The tangent is a reference axis per code (+z, −z, +x, −x, −x, +x) made perpendicular to the normal and turned about it by the angle, t0 cos a + (n × t0) sin a; the bitangent is sign * (n × t). On the LOD-0 sections of 120 stock Quick Drop meshes, the decoded normal lies along the face normal (median dot 0.998), the tangent along increasing U (median 1.000) and the bitangent along increasing V (median 0.998). The zero of the 9-bit components is the value 256: over 8,818,960 tangent frames in the 8,629 main-pass sections of the first 6,000 MeshSets read, the 9-bit minor component has the value 256 in 4,188,116 of 17,637,920 components, against 255 in 947,800 and 257 in 179,223, and the most common frame has both minor components 256 (1,055,763 frames, normals along an axis). Observed Re-encoding gives back 22,085 of 23,704 stored frames of 60 one-LOD meshes byte for byte; the rest are the same frame under its second encoding, at a quadrant edge or between two axes. Observed

The LOD switch distances are not stored in the MeshSet: the sampled community level Numbani has zero LOD-distance float fields. Distance policy comes from the mesh’s LodGroup. Observed

San Van’s level root uses 230 stock LodGroups across 9,579 MeshSets, which have one to six LODs (762 / 3,527 / 4,009 / 1,145 / 99 / 37). Lod1 is mostly 180, 45, 90 or 112 m, the last switch mostly 180 or 90 m and the terminal distance 1,000 m. CullScreenArea is 0.005–0.02; about 2,000 meshes set a CullDistance enum value 1–4 (detail, short, medium, long cull), 420 set a ShadowDistance of 5–80 m, and the level has 3,021 DingoDetailFadeComponentData. Observed

The stock WorldObject_MediumVista_2Lod group is Lod1 90 m, Lod2 1,000 m, CullScreenArea 0.005 and WpsDistance 4; DingoVistaLodGroup is Lod1 10,000 m with CullScreenArea 0. Observed

WpsDistance Meaning Confidence
0 Persistent Inferred
1 ExtraLongCull Inferred
2 LongVista Observed
3 LongCull Inferred
4 MediumVista Observed
5 MediumCull Observed
6 ShortCull Observed
7 DetailCull Observed