Skip to content

Mesh and MeshData

AnvilGeometryApplies toRRGRBSWS

A Mesh holds compiled vertex streams, triangle indices, primitive partitions and material references. Riders Republic has ranged and inline compiled layouts; the latter can contain several objects sharing buffers. Observed · RR Breakpoint also stores primitives with material assignments and several packed vertex formats, including formats with up to five UV sets. The Riders Republic offsets below are not established for Breakpoint. Sourced · GRB · GRB modding reference

only in SWS

A drawable mesh is a Mesh object that holds a CompiledMesh inline: the vertex and index buffers, the draw ranges, the bone palettes and the list of material ids are all in the mesh’s own body. 221 meshes also keep their raw SubMesh objects, the mesh as authored, ahead of their bones. The 33 data forges of the PC build hold 16,149 Mesh objects, and a strict reading reads every one to its exact end with the layouts below. Proven · SWS[all 16,149 meshes of the install]

Mesh body (Shaun White Skateboarding)variableSWS100% known

Little-endian. Body offsets for a mesh without SubMeshes; n is the bone count, L the compiled data's length, m the material count. A mesh with SubMeshes stores them at 18, and every later offset moves by their length.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x01u8 = 1managedProven1 on every mesh
0x14u32idProventhe object's id in its entry's directory
0x54u32 = 0x415D9568classProvenCRC-32 of Mesh
0x94u32CategoryObservedenum
0xD1u8GeneratedObserved
0xE4u32SubMeshes count sProven0 on 15,928 meshes; 1 to 5 on 221. When it is not 0, s SubMeshes follow here, each a pointer tag 0 (inline) and a SubMesh object (below), before the bone count
0x124u32Bones count nProven
0x1676nMeshBone[n]bonesProvensee the MeshBone layout
22+76n11bytesunknownUnknowna byte 0 (14,199 meshes) or 1 (1,950), a byte 1, then nine zeros, on every mesh
33+76n4u32 = 0xFC9E1595CompiledMesh classProvenCRC-32 of CompiledMesh
37+76n4u32Data length LProven
41+76nLbytesDataProventhe compiled buffers, below
41+76n+L4u32 = 16PlatformVersionProven16 on every mesh
45+76n+L4u32 = 32SDKVersionProven32 on every mesh
49+76n+L4u32material count mProvenequals the range count A on every mesh
53+76n+L4mu32[m]material idsObservedbare ids of the material objects; material i is drawn by range i
MeshBone (Shaun White Skateboarding)76 bytesSWS11% known

Little-endian, inline in the mesh body.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32idProven
0x44u32 = 0x9EF0E7A1classProvenCRC-32 of MeshBone, on every bone
0x84u32bone nameInferreda hash; consecutive bones of one mesh differ in it
0xC64f32[16]matrixUnknowna 4 x 4 matrix; which space it maps is not established
CompiledMesh dataLSWS92% known

Little-endian. Offsets relative to the start of Data. Every relation in the notes holds on all 16,149 meshes.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 16versionProven16 on every mesh
0x44u32extra stream count sProven0 on every mesh without SubMeshes; 0 to 3 on meshes with SubMeshes, where it equals the most MorphChannels any of its SubMeshes holds (morph targets)
0x84u32strideProvenbytes per vertex: the vertex stride the game binds
0xC4u32vertex bytes VProventhe bytes of one stream: the size of the game's vertex buffer
0x104u32stride x (s + 1)Proven
0x144u32V x (s + 1)Proven
0x184u32index bytes IProventhe size of the game's index buffer
0x1C4u32 = 0unknownUnknown0 on every mesh
0x204u32flagsProvenselects the vertex elements; see vertex formats
0x244u32range count AProven
0x284u32range count BProven
0x2C4u32totalProvenV x (s + 1) + I + 24 x (A + B)
0x30V x (s + 1)bytesvertex streamsProvenstream 0 is the base mesh, uploaded unchanged as the vertex buffer; each further stream is a whole vertex array of the same layout
48+V(s+1)Iu16[]indicesProvenuploaded unchanged as a 16-bit index buffer
48+V(s+1)+I24 x (A + B)range[A + B]rangesProvenA first-partition ranges, then B second-partition ranges
total+4840AbytespalettesProven40 bytes per first-partition range, starting with one palette per range

The game uploads a mesh’s data unchanged. Running the game’s own loader and renderer from a new game to the start of the level-02 load, 615 vertex and index buffers created for 516 meshes held exactly the stored stream-0 vertex bytes and index bytes, and every indexed draw used 16-bit indices. Observed · SWS[every mesh the game loaded on that route]

Range (Shaun White Skateboarding)24 bytesSWS83% known

Little-endian. One DrawIndexedPrimitive call.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 4primitive typeProvena triangle list, on every range
0x44u32first vertexObservedthe draw's MinIndex
0x84u32vertex countObservedthe draw's NumVertices
0xC4u32first indexObservedthe draw's StartIndex, in indices
0x104u32triangle countObservedthe draw's PrimitiveCount
0x144u32unknownUnknownvalues vary without pattern; not passed to the draw

Each range is one draw. On the traced route every draw of a level-02 mesh is one of its first-partition ranges exactly, with MinIndex the first vertex, NumVertices the vertex count, StartIndex the first index, PrimitiveCount the triangle count and a base vertex of 0, drawn as a triangle list. No draw on that route used a second-partition range. Observed · SWS[every draw of a level-02 mesh on that route]

The first A ranges partition the index buffer one-to-one with the mesh’s materials (range i uses material i), and the B ranges are a second partition of the same indices: in each, the triangles add up to the index count, and every range’s vertices lie inside the vertex stream. Proven · SWS[every mesh of the install] The second partition has as many ranges as the first on 10,344 meshes and fewer on 5,805 (often one range over all the indices). Proven · every mesh of the install

Bone palette (Shaun White Skateboarding)7 + nSWS71% known

One per first-partition range, back to back from the start of the palettes; the rest of the 40 bytes per range is left as stored.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x01u8 = 3markerProven3 on all 34,517 palettes
0x11u8 = 1versionProven1 on all palettes
0x21u8entry count nProven0 on meshes without bones; at most 32
0x32u16unknownUnknownthe range's position on 25,532 of 34,517 palettes, another value on the rest
0x52u16vertex countProvenequal to the range's vertex count on every palette
0x7nu8[n]bonesInferredindices into the mesh's bones, all below the bone count; the range's blend indices index this list

The bytes after the palettes, up to 40 per range, are stored but follow no layout that is established; in the meshes examined they look like left-over index and vertex values. Unknown

A SubMesh is one part of the mesh as authored, before it is compiled: one material’s triangles, with full-precision normals, every UV channel and the morph targets of that part. Each SubMesh owns a copy of itself that adds tangents and binormals and drops the morph targets. Each SubMesh corresponds to one first-partition range of the CompiledMesh, the one drawn with the SubMesh’s material: the range holds the SubMesh’s vertex positions bit for bit and the same triangles, in another vertex and triangle order, and the mesh’s vertex count is the SubMeshes’ vertex counts summed. Proven · SWS[all 349 SubMeshes of the 221 meshes] The ranges come in the SubMeshes’ order on 214 of the 221 meshes. Proven · all 221 meshes

SubMesh (Shaun White Skateboarding)variableSWS69% known

Little-endian, inline in the mesh body after its pointer tag. n is the vertex count (the positions' count), t the triangle count; every run is a u32 count and its elements. Offsets are from the managed byte; a variable-length run moves everything after it.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x01u8 = 1managedProven1 on every SubMesh
0x14u32idProventhe object's id
0x54u32 = 0x5755DE7FclassProvenCRC-32 of SubMesh
0x94u32 = 0unknownUnknown0 on every SubMesh; the class has a TriangleType enumeration (TriangleStrip, TriangleList), and the indices are a triangle list
0xD4 + 6tu16 runindicesProven3t vertex indices, a triangle list: the triangles of the SubMesh's compiled range
17+6t4 + 4n or 4u32 runsource verticesObservedn values, or none on the copy: vertices with equal values have equal positions, as if each named the authored vertex the vertex was split from
after the source vertices4u32 = 0unknownUnknown0 on every SubMesh
next4 + 4tu32 runface wordsUnknownt values, one per triangle: bit masks such as 1, 2, 4 and 8 (smoothing groups are a candidate)
next4 + tu8 runface bytesUnknownt values, one per triangle: 3 on most, 0 to 7 in all
next4 + 12nfloat3 runpositionsProvenequal bit for bit to the compiled range's positions
next4 + 12nfloat3 runnormalsObservedunit vectors; the compiled UBYTE4 normal is within one step of (n + 1) x 127 on 346 of the 349 SubMeshes
next4 + 12n or 4float3 runtangentsObservedn unit vectors perpendicular to the normals on every copy, none on every original
next4 + 12n or 4float3 runbinormalsObservedn unit vectors perpendicular to the normals on every copy, none on every original
next4 + 4n or 4u32 runcoloursObservedn D3DCOLOR values (B, G, R, A), or none: on 372 of the 698 SubMeshes (copies counted), and on all 186 originals that hold it equal to the compiled vertices' COLOR0
next4 + 4n or 4u32 runvertex wordsUnknownn values or none; present on 16 of the 698 SubMeshes (8 originals and their copies)
next4 + 4n or 4u32 runvertex wordsUnknownn values or none; present on 26 of the 698 SubMeshes, all originals
next4u32 = 0empty runUnknowna count, 0 on every SubMesh (its elements' size is unknown)
next4 + 4nu32 runvertex wordsUnknownn values on all 698 SubMeshes, colour-like (such as 0xFF000000)
next4u32 = 0empty runUnknowna count, 0 on every SubMesh
next4u32 = 0empty runUnknowna count, 0 on every SubMesh
nextvariableUVChannel runUV channelsProven1 to 3 UVChannels (below); the first is the compiled vertices' TEXCOORD0; the compiled data keeps no further UV of a morph mesh
next4u32 = 0unknownUnknown0 on every SubMesh
nextvariableMorphChannel runmorph channelsProven0 to 3 MorphChannels (below); only on the original, never on the copy
next1u8has morphsProven1 when there are morph channels, else 0, on every SubMesh
next4u32material idProventhe material id the mesh's material list gives the SubMesh's range, on every SubMesh
next2u8[2] = 1, 1unknownUnknown1 and 1 on every SubMesh
next1 or 1 + a SubMeshpointercopyObservedtag 0 and an inline SubMesh on every original, tag 3 (none) on every copy; the copy has the same indices, positions and material, as many UV channels, tangents and binormals added, no source vertices and no morph channels
next4u32unknownUnknown0 on 580 of 698 SubMeshes, hash-like values on the rest
UVChannel (Shaun White Skateboarding)variableSWS31% known

Little-endian, inline in a SubMesh's run of UV channels. n is the SubMesh's vertex count.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0idUnknown0 on every channel
0x44u32 = 0xBDAD8273classProvenCRC-32 of UVChannel
0x84u32 = 0unknownUnknown0 on every channel; the class has a UVType enumeration (UV_Lightmap, UV_Layer, UV_Normal)
0xC1u8unknownUnknown0 on 750 of the 934 channels, other values on the rest
0xD4 + 8nfloat2 runUVsProvenn UVs; the first channel's equal the compiled TEXCOORD0 bit for bit on 347 of 349 SubMeshes
17+8n4 + 4n or 4u32 runsourceUnknownn values, or none (on the copy's channels)
MorphChannel (Shaun White Skateboarding)16 + 32nSWS67% known

Little-endian, inline in a SubMesh's run of morph channels. n is the SubMesh's vertex count.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32idProventhe object's id, not 0
0x44u32 = 0xE70C5754classProvenCRC-32 of MorphChannel
0x84u32unknownUnknownhash-like
0xC4 + 12nfloat3 runpositionsObservedabsolute positions of the morph target: equal bit for bit to compiled stream k + 1 on 369 of 375 channels
16+12n4 + 12nfloat3 runnormalsObservedthe morph target's normals
20+24n4 + 8nfloat2 runUVsObservedequal to the SubMesh's first UV channel on 293 of 375 channels

Channel k of a SubMesh is morph stream k + 1 of the CompiledMesh: on 369 of the 375 channels its positions equal that stream’s positions at the matching vertices bit for bit, and the other six (of two meshes) differ from it in one coordinate. Observed · SWS[all 375 morph channels, vertices matched through the triangles]

The game draws these meshes from their CompiledMesh. On the traced route it loaded 14 of them; for 7 it created a vertex buffer holding the stored stream-0 bytes and, as further vertex buffers, every morph stream’s bytes unchanged, and it drew 5, binding stream 0 and the per-instance colour only. Observed · SWS[the traced route]

The further vertex streams are shape morphs of the gameplay objects that grow or deform (the InfGP bump, vert and pool shapes). Of 26 morph meshes in level 01, 30 morph targets in all, eight further streams also change the UVs or colours. Observed · level 01

What do the SubMesh’s unknown runs and words hold?

The per-triangle words and bytes, the three runs of per-vertex words after the colours, the source runs of the UV channels, the MorphChannel’s word after its class and the SubMesh’s last word are read as stored; their meaning is not established. Three counts are 0 on every SubMesh, so their elements’ size is not known. Why each SubMesh keeps a copy that differs from it by tangents, binormals and the missing morph channels and source runs is not established. Unknown

Offsets are relative to the resource body, after its resource identity and type header. The compiled marker is at M = 61 + 81 × boneCount, where boneCount is the little-endian u32 at body offset 16. Proven · RR · all mesh headers

The 16 bytes before the bone count are, in the runtime class registry’s order, the ManagedObject byte, DescriptorMask, Generated, VertexStride and MaxCullingDistance (f32), then eight bytes that are zero on every header and that the registry’s list does not itemise. DescriptorMask takes the values 0, 1, 2, 8, 16, 17, 18, 32, 64 and 128; MaxCullingDistance values include 0, 100, 660, 820 and 2,580. Observed · RR · 85,577 Mesh headers

Each 81-byte bone record before M is a MeshBone; see vertex formats for its fields. Observed · RR

Compiled mesh prefixvariableRR53% known

Little-endian; offsets from M, not the resource body.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0xFC9E1595CompiledMesh markerObserved
0x41u8layout selectorObserved0 ranged; 3 inline
0x58bytesstored prefixUnknownzero in the split and inline multi-mesh census
0xD4u32layout classObserved0xC351EE43 ranged; 0x0645ABB5 MeshData for inline

The ranged prefix continues with a u32 at M+17, vertex-format byte at M+21, three u32 stream strides at M+22, a u32 at M+34, centre and half-extent float3 values at M+38 and M+50, a byte at M+62, range count at M+63, and eight bytes before the first range at M+75. Proven · RR · 29,174 split meshes

Ranged compiled header after the layout class58 bytesRR71% known

Offsets start at M+17; measured over all 29,174 split meshes.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32stored wordUnknown2 throughout the split census
0x41u8vertex formatProven
0x512u32[3]stream stridesProven
0x114u32stored wordUnknown0 throughout the split census
0x1512float3centreProven
0x2112float3half extentProven
0x2D1u8stored byteUnknown0 throughout the split census
0x2E4u32range countProven
0x328bytesstored wordsUnknowntwo zero u32 values throughout the split census
Format Kind Stream strides in bytes Confidence
static16 11 16, 0, 0 Observed · RR
static24 2 24, 0, 0 Observed · RR
static28 3 28, 0, 0 Observed · RR
split12 0 12, 12, 8 Proven · RR · all split12 meshes
split24 1 24, 12, 8 Proven · RR · all split24 meshes
split12 with eight influences 0 12, 12, 16 Proven · RR · all 58 meshes

Each range starts with 0x07B19A87, a byte 1, three bytes, first-index u32 and vertex-base u32. Every range except the last has another eight bytes. The first range begins at index and vertex zero; successive range boundaries partition the buffers. Indices are u16 and relative to each range’s vertex base. Proven · RR · 29,174 split meshes

Ranged range record16 bytesRR75% known

Eight further bytes follow every record except the last.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0x07B19A87range classProven
0x41u8stored byteUnknown1 in the split census
0x53bytesstored bytesUnknownzero in the split census
0x84u32first indexProven
0xC4u32vertex baseProven

After the ranges come four length-prefixed streams in order: primary attributes, positions, skin, indices. A static mesh’s separate position and skin streams have zero lengths. Split position records are 12 bytes; skin records are 8 or 16 bytes. Proven · RR · split census

The index buffer is followed by a length-prefixed block of 32 bytes per range (PrimitiveDescData, below), two more length-prefixed streams, MeshData, the primitive records, four length-prefixed runs and the material list. All four final runs are empty in every split mesh; some static meshes carry copies of their vertex and index buffers there. Proven · RR · 29,174 split bodies; static copies observed

PrimitiveDescData record32 bytesRR100% known

One per range, after the index buffer. Field names from the runtime class registry.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x012float3centreObservedequals the compiled header's centre on all 1,820 checked single-range meshes
0xC12float3half extentObservedequals the compiled header's half extent on the same 1,820
0x181u8primitive typeObserved1 in the checked records
0x193u24index countObservedthree times the range's triangle count on 5,205 of 5,205 checked
0x1C4u32first indexObservedequals the range's first index on 2,617 of 2,617 checked
MeshData declaration and count10 bytesRR100% known

Starts immediately after the MeshData marker 0x0645ABB5.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x01u8index width selectorObserved0 means u16; 1 means u32
0x11u8vertex formatObserved
0x23u8[3]stream stridesObserved
0x51u8NbBonesPerVertexObservedregistry name; 4 in static24 and four-influence split meshes, 8 in eight-influence split meshes
0x64u32object countObserved
Inline object record36 bytesRR56% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x08u64stored identityUnknownzero in all 2,732 multi-mesh object records
0x84u32 = 0xA57387EFMeshPrimitive markerProven
0xC4u32vertex baseProven
0x104u32stored wordUnknownzero in all 2,732 records
0x144u32vertex countProven
0x184u32first indexProven
0x1C4u32triangle countProven
0x204u32stored wordUnknown1 in all 2,732 records; material-slot interpretation not established

The inline form has MeshData directly at M+13. Its declaration, count and all object records precede the length-prefixed primary, position, skin and index blobs; the material list follows the indices. One object is a special case of this grammar. Proven · RR · 1,170 multi-mesh bodies

Every one of the 1,170 multi-mesh entries contains two to four objects, uses 24-byte vertices and u16 indices, and has empty separate position and skin streams. Object vertex and index intervals tile their shared buffers in record order. Their indices are absolute, unlike the ranged form: adding the vertex base changes the result incorrectly. Independent decoding agrees on positions, indices and object partitions on all 1,170 entries, and each body is consumed exactly. Proven · RR

The common material list is a u32 count, eight bytes, one 28-byte descriptor per range, another u32 count, six bytes, one 31-byte group per range and a 20-byte trailer. Each descriptor starts with 0x0B1D34C1, four bytes, zero byte, u16 vertex count, zero byte, u64 Material identity and eight bytes. The group’s identity at offsets 8 and 17 repeats the descriptor’s identity. Proven · RR · split and inline multi-mesh censuses

Mesh material descriptor28 bytesRR79% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0x0B1D34C1descriptor markerProven
0x44bytesstored bytesUnknownnot interpreted
0x81u8stored byteUnknown0 in the measured layouts
0x92u16vertex count modulo 65536Observed
0xB1u8stored byteUnknown0 in the measured layouts
0xC8u64Material identityProven
0x148bytesfinal bytesObservedlast split descriptor has u32 31 then f32 position scale; others are zero

The descriptor vertex count is the range count modulo 65,536, rather than a limit on the mesh’s number of vertices. Two sampled inline meshes contain 72,260 and 93,906 vertices. Observed · RR

The count can also be stale. GFX_WireFrame_Wall_01_LOD0 declares 8,845 vertices and 5,593 triangles in both its primitive record and its material descriptor, while its buffers hold 2,490 vertices and 1,992 triangles; the decoded positions agree with the stored bounds, and an independent reader decodes the same buffers. The buffers, not the declared count, describe that mesh. Observed · RR

The runtime class registry names the compiled structures, and its field order agrees with the stored bytes. Observed · RR · 85,577 Mesh headers and 83,800 ranged bodies

Stored structure Registry class or field Confidence
Ranged layout class 0xC351EE43 ClusteredMeshData, the compiled mesh’s pointer; the inline form stores it null, so its selector byte is the pointer tag Observed · RR
Ranged header words Version (2), VertexFormat, three strides, ClusterCount (0), Center, HalfExtend, IsFixedClusterSize (0), DrawPrims count Observed · RR
Range record 0x07B19A87 ClusteredPrimitiveInfo: ClusterCount (1), IndexStart, VertexStart Observed · RR
Streams VertexDataStatic, VertexDataDynamic (positions), VertexDataIndexWeight (skin), IndexData, PrimitiveDescData Observed · RR
Inline object record MeshPrimitive: MinIndex, IsUsingDepthOnlyBuffers (0), NumVertices, StartIndex, PrimitiveCount, Type (1) Observed · RR
Final four runs VertexBufferDataStatic, VertexBufferDataDynamic, VertexBufferDataIndexWeight, IndexBufferData; the middle two are empty on all 83,800 bodies, the first and last filled on 2,160 Observed · RR
Descriptor 0x0B1D34C1 MeshInstancingData; the last descriptor’s u32 31 is PlatformVersion (31 on all 83,800) and its f32 is QuantizationFactor, the position scale Observed · RR
31-byte group DynamicReference 0x9F1640AF, whose ObjectReference and ObjectHandle both equal the descriptor’s Material on all 127,219 groups; six bytes remain unattributed Observed · RR
20-byte trailer Sixteen Mesh bool bytes, then u32 UserCategory Observed · RR

The first trailer bool is 1 on exactly the 2,160 meshes with filled CPU-side runs, which fits a flag such as Dynamic; the individual names of these 16 bytes are not established. Observed · RR Inferred · RR · flag name

See vertex formats for stream decoding and LOD levels for geometry selection.

What do the remaining compiled fields control?

The values retained around stream declarations, object records and the trailer do not all have established runtime meanings. A constant value alone does not establish a reserved field. Unknown · RR