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

NameElement TypeElement CountAlignment
Headerx42Header_v6_t1None, appears at beginning of file.
Pack Headerx42PackHeader_v6_t1None, appears immediately after header.
Bonesx42Bone_v5_theader.numBones8
Animation Groupsx42AnimGroup_v5_theader.numAnimGroups8
Position Palettevec3_t packed into u16[3]header.numPosValues2
Scale Palettevec3_t packed into u16[1] or u16[3] (see Model Flags)header.numScaleValues2
Rotation Palettequat_t packed into s16[4]header.numRotValues2
Key Streamx42KeyStreamEntry_v5_theader.keyStreamLength8
Animationsx42Animation_v5_theader.numAnims8
Tagsx42Tag_v5_theader.numTags8
Influencesx42Influence_v5_theader.numInfluences8
Lodsx42LodRange_v5_theader.numLods8
Vertex Groupsx42Group_v5_theader.numGroups8
Vertex Positionsvec3_t packed into u16[3]header.numVerts2
Vertex Blend Indices and WeightsMixed data packed into u80, 1, 3, 5, or 7 values per vertex, depending on groups1
Vertex Normalsvec3_t packed into s8[3]0 or header.numVerts (see Model Flags)1
Vertex Tangent Basis2 vec3_t and a f32 packed into s8[7]0 or header.numVerts (see Model Flags)1
Vertex Texture Coordinatesvec2_t packed into u16[2]0 or header.numVerts (see Model Flags)2
Vertex Colorsrgba_t0 or header.numVerts (see Model Flags)4
Indicesx42Index_theader.numIndices4
Morph Targetsx42MorphTarget_v6_theader.numMorphTargets8
Morph Target Indicesx42Index_theader.numMorphDeltas4
Morph Target Positionsvec3_t packed into u16[3]header.numMorphDeltas2
Morph Target Normalsvec3_t packed into s8[3]0 or header.numMorphDeltas (see Model Flags)1
Morph Target Tangent Basis2 vec3_t and a f32 packed into s8[7]0 or header.numMorphDeltas (see Model Flags)1
Stringschar stored as u8header.nameBlobLen1

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):

  1. Tangent X, Y, and Z coordinates.
  2. Normal direction multiplier.
  3. 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.