Older Versions
If you're looking for info on the older versions, you'll find that here:
X42_VER_V6
This version is basically the same as X42_VER_V5, except for the addition of morph targets.
Data Sections
The x42 file format is, essentially, a series of data arrays. Some are optional, but they are always stored in order.
Format
All of the x42 data types are defined in x42data.h. Read the Strings section for more info on x42NameIndex_t. Note that the types that are likely to change include a suffix that indicates what version they were introduced in. High version x42 files may use structures from previous versions, you'll need to read the documentation to see what's what.
Data Packing
Much of the float data in the x42 file is packed into one or two byte values. The format uses several packing schemes. Each array's description specifies the scheme used to pack its data.
Range Packing
Range packed data segments compress the range [minVal, maxVal] into the range [0, 65530], and store the result as unsigned 16-bit integers. The range itself is stored separately in the file (usually in the pack header) and is used to unpack the data. Multi-channel data is interleaved, though a separate range is used to pack each channel.
S16N Packing
S16N packed data segments compress the range [-1, 1] into the range [-32767, 32767] and store their data as signed 16-bit integers.
U8N Packing
U8N packed data segments compress the range [0, 1] into the range [0, 255] and store their data as unsigned 8-bit integers.
S8N Packing
S8N packed data segments compress the range [-1, 1] into the range [-127, 127] and store their data as signed 8-bit integers.
Alignment
The old file format versions aligned each section to a 16-byte boundary. The idea was that the file data could be used directly by simply memory-mapping the file and computing the offset to the start of each section. This turned out to be overkill, and the data section padding has been revamped.
Overview
| Name | Element Type | Element Count | Alignment |
| Header | x42Header_v6_t | 1 | None, appears at beginning of file. |
| Pack Header | x42PackHeader_v6_t | 1 | None, appears immediately after header. |
| Bones | x42Bone_v5_t | header.numBones | 8 |
| Animation Groups | x42AnimGroup_v5_t | header.numAnimGroups | 8 |
| Position Palette | vec3_t packed into u16[3] | header.numPosValues | 2 |
| Scale Palette | vec3_t packed into u16[1] or u16[3] (see Model Flags) | header.numScaleValues | 2 |
| Rotation Palette | quat_t packed into s16[4] | header.numRotValues | 2 |
| Key Stream | x42KeyStreamEntry_v5_t | header.keyStreamLength | 8 |
| Animations | x42Animation_v5_t | header.numAnims | 8 |
| Tags | x42Tag_v5_t | header.numTags | 8 |
| Influences | x42Influence_v5_t | header.numInfluences | 8 |
| Lods | x42LodRange_v5_t | header.numLods | 8 |
| Vertex Groups | x42Group_v5_t | header.numGroups | 8 |
| Vertex Positions | vec3_t packed into u16[3] | header.numVerts | 2 |
| Vertex Blend Indices and Weights | Mixed data packed into u8 | 0, 1, 3, 5, or 7 values per vertex, depending on groups | 1 |
| Vertex Normals | vec3_t packed into s8[3] | 0 or header.numVerts (see Model Flags) | 1 |
| Vertex Tangent Basis | 2 vec3_t and a f32 packed into s8[7] | 0 or header.numVerts (see Model Flags) | 1 |
| Vertex Texture Coordinates | vec2_t packed into u16[2] | 0 or header.numVerts (see Model Flags) | 2 |
| Vertex Colors | rgba_t | 0 or header.numVerts (see Model Flags) | 4 |
| Indices | x42Index_t | header.numIndices | 4 |
| Morph Targets | x42MorphTarget_v6_t | header.numMorphTargets | 8 |
| Morph Target Indices | x42Index_t | header.numMorphDeltas | 4 |
| Morph Target Positions | vec3_t packed into u16[3] | header.numMorphDeltas | 2 |
| Morph Target Normals | vec3_t packed into s8[3] | 0 or header.numMorphDeltas (see Model Flags) | 1 |
| Morph Target Tangent Basis | 2 vec3_t and a f32 packed into s8[7] | 0 or header.numMorphDeltas (see Model Flags) | 1 |
| Strings | char stored as u8 | header.nameBlobLen | 1 |
Header
The file header includes a tag that identifies a file as a x42 model (X42_IDENT), the file version number (one of the X42_VER_V* values), the model flags, and the number of elements in each file section.
Frame Numbering
The header also includes two fields that identify the valid frame range for an x42 file. The frame numbers stored in the animation data lie in the interval [0, numFrames). However, artists may prefer to work with a one-based numbering system (or perhaps something else) so the baseFrame field exists to bias the interval to [baseFrame, baseFrame + numFrames). The x42 system operates by subtracting baseFrame from any inputted frame numbers to shift the values into the zero-based range used by the key stream.
Model Flags
The x42 format includes several optional data sections, and also has the option to reduce 3-component scale data to a single component if the values are all uniform. These options are signaled by setting various model flags. The valid flags are as follows:
X42_MF_HAS_NORMALS
If this flag is set then the file includes vertex normals which may optionally be loaded. If it is absent then the vertex normals section is not written to the file (nor is alignment performed for this section). The same holds for the morph target normals section.
X42_MF_HAS_TANGENT_BASIS
If this flag is set then the file includes vertex tangents which may optionally be loaded. If it is absent then the vertex tangent basis section is not written to the file (nor is alignment performed for this section). The same holds for the morph target tangent basis section.
X42_MF_HAS_TEXTURE_COORDINATES
If this flag is set then the file includes vertex texture coordinates which may optionally be loaded. If it is absent then the vertex texture coordinates section is not written to the file (nor is alignment performed for this section).
X42_MF_HAS_COLORS
If this flag is set then the file includes vertex colors which may optionally be loaded. If it is absent then the vertex colors section is not written to the file (nor is alignment performed for this section).
X42_MF_UNIFORM_SCALE
If this flag is set then all scale values are uniform and only the x component is saved in the file. The data is expanded back out to three components on load.
Pack Header
The pack header contains the minimum and maximum values used to range pack certain data arrays. This includes the position palette, the scale palette, the vertex positions, the vertex texture coordinates, and the morph target position values.
Note that the scale palette is handled a bit differently than the rest. It only includes a single min/max range despite the fact that scale values may be 3-component quantities. If non-uniform scales are used then the same min/max range is used for all three components.
Bones
The model's bones are stored as an array of x42Bone_v5_t structures. In terms of useful data the name and parentIdx fields are the most important. flags can specify X42_BF_USE_INV_PARENT_SCALE in order to change how the bone's matrix is calculated but this value is never used by the exporter (and may be dropped in a future version). Bones also belong to logical groupings called animation groups, though this data is stored in another array.
Bones are sorted such that a bone's index is always greater than that of its parent, and bones which are in the same animation group are always adjacent.
Animation Groups
Animation groups are logical groupings of bones that can be animated semi-independently of the rest of the skeleton.
Position Palette
All of the position values referenced by the key stream are stored in the position palette. They are logically vec3_t values, however they are range packed into u16[3] elements. The X, Y, and Z components are packed based on the values in packHeader.animPosPack.
Scale Palette
All of the scale values referenced by the key stream are stored in the scale palette. They are logically vec3_t values, however they are range packed into u16[3] or u16[1] (depending on whether X42_MF_UNIFORM_SCALE is set) elements. The X, Y, and Z components are packed based on the value in packHeader.animScalePack.
Rotation Palette
All of the rotation values referenced by the key stream are stored in the rotation palette. They are logically quat_t values, however they are S16N packed.
Key Stream
Animations
Tags
Influences
Influences are critical to the x42 format as they are what binds vertices to bones. The x42Influence_v5_t is fairly simple, containing only two fields: a bone index (which may be X42_MODEL_BONE if the influence is static) and the meshToBone matrix. The matrix that vertices are weighted against is formed by concatenating the meshToBone matrix onto the referenced bone's model space matrix.
Lods
x42 files can contain several levels of detail. LODs are numbered such that zero is the most detailed and higher-numbered LODs are less detailed. The vertex groups array is sorted by LOD. The LODs array contains an x42LodRange_v5_t element for each LOD which specifies the range of groups that belong to that LOD.
Vertex Groups
Each x42Group_v5_t structure contains most of the data that's required to set up and make an OpenGL or Direct3D draw call. It contains the preferred material name, the surface name (which the engine can use for any purpose), a palette of influences that affect the group's geometry, a range of vertices and indices to draw, and the topology of that range.
The group structure also contains some optimization hints.
Group Influences
Each group supports an influence palette that can be up to 60 (X42_MAX_INFLUENCES_PER_BATCH_V5) influences long. The group's vertices index into this palette (and not into the model-wide influences array). The numInfluences and influences (which indexes into the model-wide influences) fields describe the palette.
Group Influences per Vertex
Many groups don't make use of the full four weights per vertex that the format supports. This information is signaled by the value of the maxVertInfluences field. Only the first maxVertInfluences weights on each vertex need to be processed. The rest are guaranteed to have zero weights.
Zero is a special case that indicates that the group's vertices are all 100% weighted to a single influence and that this influence will always be an identity matrix. In other words, this group's vertices don't need to be skinned in any way before they are rendered as they are already in their correct model-space positions.
Group Topology
The x42 format supports point, line, and triangle topologies in both list and strip formats. The primType field will be one of the valid primitive type constants:
- X42_PT_POINT_LIST
- X42_PT_LINE_LIST
- X42_PT_LINE_STRIP
- X42_PT_TRIANGLE_LIST
- X42_PT_TRIANGLE_STRIP
- X42_PT_TRIANGLE_FAN
Group Vertices
Each group uses a unique range of vertices. That range is the vertices indexed by the interval [firstVert, firstVert + numVerts).
Group Indices
Group topologies may be either indexed or non-indexed. If the group is non-indexed then the firstIndex field will be set to X42_NO_INDICES. Note that groups with the X42_PT_POINT_LIST topology may not be indexed.
If a group is indexed then firstIndex is an offset into the x42/format#Indices array. Indices are always relative to the group's range of vertices, so if firstVert is 8 then an index value of 4 would address element 12 in the vertex data arrays (which is the fourth vertex in from the sub-array starting on element 8).
Vertex Positions
The model's pre-skinning (T pose) vertex positions are stored in range packed format. The ranges for the x, y, and z components are stored in packHeader.vertPosPack.
Vertex Blend Indices and Weights
The x42 format stores as little data in this section as possible. To do this the data for each group is packed separately, and the format of that data depends on the group's maxVertInfluences value.
If maxVertInfluences is zero, then no data is stored (since a value of 0 implies that all vertices are already 100% weighted to the group's only (identity) influence.
Otherwise, the file contains maxVertInfluences * 2 - 1 bytes per vertex. The bytes are index/weight pairs (weights are U8N-packed). The last weight is not stored since it can be inferred from the others (because the sum of the weights must be one).
Vertex Normals
Vertex normals are S8N packed. This data section only appears if the X42_MF_HAS_NORMALS flag is set in the model flags.
Vertex Tangent Basis
Vertex tangents and bitangents are S8N packed. A "normal direction" element is sandwiched between the two such that each element packs to the following seven bytes (in order):
- Tangent X, Y, and Z coordinates.
- Normal direction multiplier.
- Bitangent X, Y, and Z coordinates.
The normal direction multiplier unpacks to either positive or negative 1, such that the normal can be reconstructed from the tangent and bitangent as cross( tan, bit ) * normDir.
This data section only appears if the X42_MF_HAS_TANGENT_BASIS flag is set in the model flags.
Vertex Texture Coordinates
The texture coordinates are range packed. The ranges for the u and v components are stored in packHeader.vertTcPack.
This data section only appears if the X42_MF_HAS_TEXTURE_COORDINATES flag is set in the model flags.
Vertex Colors
Vertex colors are saved as raw rgba_t (u8[4]) values. As the name suggests, the bytes, in order, are the red, green, blue and alpha components of the color.
Indices
Indices are stored as unsigned 16-bit integers (libx42 uses the x42Index_t typedef to allow this to change in the future, though it is unlikely it ever will). Topologies that require more than 65535 vertices must split their triangles into multiple groups.
Morph Targets
Morph Target Indices
Morph Target Positions
Morph Target Normals
Morph Target Tangent Basis
Strings
The x42 format used to store strings as inline arrays wherever they were needed (bone names, material names, etc). There were several problems with this approach:
- Names were limited to 64 characters (actually 63, since one byte was always reserved for the null terminator).
- Short names wasted space.
- Structures containing strings were less efficient since the 64-byte name field made them larger than a cache line.
To remedy this X42_VER_V5 introduces the x42NameIndex_t type. A name index is simply a u16 offset into the strings blob which is stored at the end of the file. Each string is null-terminated.
