Model file infomation (_arc) - enenra/x4modding GitHub Wiki
File information _arc had linked to in his topic. Putting it here for now so it doesn't get lost.
XMF file info
.xmf files contain non-NPC meshes: ships, station parts, asteroids etc. They can be somewhat animated through .ani files
(moving/rotating/scaling the entire mesh over time), but do not contain bones or morph targets. They are quite close to
DirectX: their content, once decompressed, can be directly copied to a DirectX vertex or index buffer.
Each file consists of a header, a number of buffer descriptions, and an equal number of buffer contents. A buffer can be
a vertex buffer (vertex positions/normals/texture coordinates/...) or an index buffer (indices into the vertex buffer
which determine the polygons). There can be one vertex buffer that stores all attributes of all vertices, or multiple
vertex buffers where each buffer stores one particular attribute for all vertices.
Collision meshes (*-collision.xmf) must have exactly one vertex attribute, namely D3DDECLUSAGE_POSITION (0) stored as
D3DDECLTYPE_FLOAT3 (2). This attribute must be stored in the type/usageIndex/format fields of the DataBufferDesc structure,
not in the VertexDeclElement array. Not following this rule causes the game to crash.
byte magic[4] = "XUMF" // Xu Mesh File
byte version = 3
byte bBigEndian = 0
byte dataBufferDescOffset = 0x40 // file offset of first data buffer desc
byte pad
byte numDataBuffers // typically 2, but may be more. there is always one index buffer and one or more vertex buffers.
// if there are multiple vertex buffers, these merely define additional attributes for the same
// vertices, not more vertices.
byte dataBufferDescSize // size of one data buffer description. has a maximum value of 0xBC (= sizeof(DataBufferDesc)), but some files use less,
// in which case the remaining DataBufferDesc fields should be set to 0.
byte numMaterials
byte materialSize = 0x88 // size of one material assignment
byte pad[10]
int32 primitiveType = 4 // D3DPRIMITIVETYPE; determines how the index buffer is used to draw polygons. 4 = triangle list
byte pad[dataBufferDescOffset-0x1A]
DataBufferDesc[numDataBuffers] // describes a DirectX 9 vertex or index buffer
int32 type // 0x1E -> index buffer, otherwise vertex buffer
// For vertex buffers with numVertexElements = 0, indicates the usage of a single implicit element. The type
// should be mapped to a D3DDECLUSAGE as follows:
// type D3DDECLUSAGE
// ------------------------
// 0, 1 POSITION
// 2, 3 NORMAL
// 4 TANGENT
// 5 BINORMAL
// 8 COLOR
// 20 PSIZE
// others TEXCOORD
int32 usageIndex // for vertex buffers with numVertexElements = 0, indicates the usage index of the single implicit element
int32 dataOffset // file offset = dataBufferDescOffset + numDataBuffers*dataBufferDescSize + numMaterials*materialSize + dataOffset
int32 bCompressed // if 1, the data is compressed using zlib's compress() function and can be uncompressed with uncompress()
byte pad[4]
int32 format // for index buffers, this is the index format: 0x1E -> 16-bit indices, 0x1F -> 32-bit indices.
// for vertex buffers with numVertexElements = 0, this is the type (D3DDECLTYPE) of the single implicit element.
// otherwise 0x20.
int32 compressedDataSize // size of the buffer data in the file. equal to uncompressed size if bCompressed = 0
int32 itemsPerSection // number of vertices/indices
int32 itemSize // size in bytes of a single vertex/index
int32 numSections = 1
byte pad[16]
int32 numVertexElements // for the vertex buffer, number of elements in the DirectX vertex declaration. May be 0,
// in which case a single declaration is built using type, usageIndex and format.
VertexDeclElement[16] // fixed size array, only the first numVertexElements items are used. each item is mapped to a D3DVERTEXELEMENT9
int32 type // D3DDECLTYPE
byte usage // D3DDECLUSAGE
byte usageIndex
byte pad[2]
Material[numMaterials]
int32 firstIndex // index of the first index in the index buffer which the material applies to (multiple of 3)
int32 numIndices // number of indices, starting at firstIndex, which the material applies to (multiple of 3)
// the sum of the numIndices of all Materials should be equal to the itemsPerSection of the index buffer
char name[128] // materialCollection + "." + materialName as found in material_library.xml
for each data buffer:
byte bufferData[compressedDataSize] // content of the DirectX vertex/index buffer, zlib-compressed if bCompressed = 1.
// the uncompressed size is numSections * itemsPerSection * itemSize.
XAC file info
vec3d = float x, float y, float z
vec4d = float x, float y, float z, float w
quat = float x, float y, float z, float w
string = uint32 len, char[len]
matrix44 = vec4d col1, vec4d col2, vec4d col3, vec4d pos
file:
byte magic[4] = 58 41 43 20 ("XAC ")
byte majorVersion = 1
byte minorVersion = 0
byte bBigEndian
byte multiplyOrder
chunk[...]
int32 chunkType
int32 length (sometimes incorrect!)
int32 version
byte data[length]
chunk 7: metadata (v2)
uint32 repositionMask
1 = repositionPos
2 = repositionRot
4 = repositionScale
int32 repositioningNode
byte exporterMajorVersion
byte exporterMinorVersion
byte unused[2]
float retargetRootOffset
string sourceApp
string origFileName
string exportDate
string actorName
chunk B: node hierarchy (v1)
int32 numNodes
int32 numRootNodes (number of nodes with parentId = -1)
NodeData[numNodes]
quat rotation
quat scaleRotation
vec3d position
vec3d scale
float unused[3]
int32 -1 (?)
int32 -1 (?)
int32 parentNodeId (index of parent node or -1 for root nodes)
int32 numChildNodes (number of nodes with parentId = this node's index)
int32 bIncludeInBoundsCalc
matrix44 transform
float fImportanceFactor
string name
chunk D: material totals (v1)
int32 numTotalMaterials
int32 numStandardMaterials
int32 numFxMaterials
chunk 3: material definition (v2)
vec4d ambientColor
vec4d diffuseColor
vec4d specularColor
vec4d emissiveColor
float shine
float shineStrength
float opacity
float ior
byte bDoubleSided
byte bWireframe
byte unused
byte numLayers
string name
Layer[numLayers]:
float amount
float uOffset
float vOffset
float uTiling
float vTiling
float rotationInRadians
int16 materialId (index of the material this layer belongs to = number of preceding chunk 3's)
byte mapType
byte unused
string texture
chunk 1: mesh (v1)
int32 nodeId
int32 numInfluenceRanges
int32 numVertices (total number of vertices of submeshes)
int32 numIndices (total number of indices of submeshes)
int32 numSubMeshes
int32 numAttribLayers
byte bIsCollisionMesh (each node can have 1 visual mesh and 1 collision mesh)
byte pad[3]
VerticesAttribute[numAttribLayers]
int32 type (determines meaning of data)
0 = positions (vec3d)
1 = normals (vec3d)
2 = tangents (vec4d)
3 = uv coords (vec2d)
4 = 32-bit colors (uint32)
5 = influence range indices (uint32) - index into the InfluenceRange[] array of chunk 2, indicating the bones that affect it
6 = 128-bit colors
typically: 1x positions, 1x normals, 2x tangents, 2x uv, 1x colors, 1x influence range indices
int32 attribSize (size of 1 attribute, for 1 vertex)
byte bKeepOriginals
byte bIsScaleFactor
byte pad[2]
byte data[numVertices * attribSize]
SubMesh[numSubMeshes]
int32 numIndices
int32 numVertices
int32 materialId
int32 numBones
int32 relativeIndices[numIndices] (actual index = relative index + total number of vertices of preceding submeshes. each group of 3 sequential indices (vertices) defines a polygon)
int32 boneIds[numBones] (unused)
chunk 2: skinning (v3)
int32 nodeId
int32 numLocalBones (number of distinct boneId's in InfluenceData)
int32 numInfluences
byte bIsForCollisionMesh
byte pad[3]
InfluenceData[numInfluences]
float fWeight (0..1) (for every vertex, the resulting transformed position is calculated for every influencing bone;
int16 boneId the final position is the weighted average of these positions using fWeight as weight)
byte pad[2]
InfluenceRange[bIsForCollisionMesh ? nodes[nodeId].colMesh.numInfluenceRanges : nodes[nodeId].visualMesh.numInfluenceRanges]
int32 firstInfluenceIndex (index into InfluenceData)
int32 numInfluences (number of InfluenceData entries relevant for one or more vertices, starting at firstInfluenceIndex)
chunk C: morph targets (v1)
int32 numMorphTargets
int32 lodMorphTargetIdx (presumably always 0; this is the index of a *collection* of numMorphTargets morph targets, not an
individual target, and an EmoActor only has one such collection)
MorphTarget[numMorphTargets]
float fRangeMin (at runtime, fMorphAmount must be >= fRangeMin)
float fRangeMax (at runtime, fMorphAmount must be <= fRangeMax)
int32 lodLevel (LOD of visual mesh; presumably always 0)
int32 numDeformations
int32 numTransformations
int32 phonemeSetBitmask (indicates which phonemes the morph target can be used for - facial animation)
0x1: neutral
0x2: M, B, P, X
0x4: AA, AO, OW
0x8: IH, AE, AH, EY, AY, H
0x10: AW
0x20: N, NG, CH, J, DH, D, G, T, K, Z, ZH, TH, S, SH
0x40: IY, EH, Y
0x80: UW, UH, OY
0x100: F, V
0x200: L, EL
0x400: W
0x800: R, ER
string name
Deformation[numDeformations]
int32 nodeId
float fMinValue
float fMaxValue
int32 numVertices
DeformVertex16 positionOffsets[numVertices]
uint16 x (fXOffset = fMinValue + (fMaxValue - fMinValue)*(x / 65535); vecDeformedPos.fX = vecPos.fX + fXOffset*fMorphAmount)
uint16 y
uint16 z
DeformVertex8 normalOffsets[numVertices]
byte x (fXOffset = x/127.5 - 1.0; vecDeformedNormal.fX = vecNormal.fX + fXOffset * fMorphAmount)
byte y
byte z
DeformVertex8 tangentOffsets[numVertices] (offsets for first tangent)
uint32 vertexIndices[numVertices] (index of the node's visual mesh vertex which the offsets apply to)
Transformation[numTransformations] (appears to be unused, i.e. numTransformations = 0)
int32 nodeId
quat rotation
quat scaleRotation
vec3d pos
vec3d scale
XPM file info
string = uint32 len, char[len]
file:
byte magic[4] = 58 50 4D 20 ("XPM ")
byte majorVersion = 1
byte minorVersion = 0
byte bBigEndian
byte pad
chunk[...]
int32 chunkType
int32 length
int32 version
byte data[length]
chunk 65: metadata (v1)
int32 fps
byte exporterMajorVersion
byte exporterMinorVersion
byte pad[2]
string sourceApp
string origFilePath
string exportDate
string motionName
chunk 66: deformation animation (v1)
int32 numMorphTargetAnims
MorphTargetAnim[numMorphTargetAnims]:
float fPoseWeight
float fMinWeight
float fMaxWeight
int32 phonemeSetBitmask (see .xac)
int32 numKeys
string morphTargetName
Key[numKeys]:
float fTime
uint16 amount (/ 65535)
byte pad[2]
XSM file info
vec3d = float x, float y, float z
quat16 = int16 x, int16 y, int16 z, int16 w; fX = x / 32767
string = uint32 len, char[len]
file:
byte magic[4] = 58 53 4D 20 ("XSM ")
byte majorVersion = 1
byte minorVersion = 0
byte bBigEndian
byte pad
chunk[...]
int32 chunkType
int32 length
int32 version
byte data[length]
chunk C9: metadata (v2)
float unused = 1.0f
float fMaxAcceptableError
int32 fps
byte exporterMajorVersion
byte exporterMinorVersion
byte pad[2]
string sourceApp
string origFileName
string exportDate
string motionName
chunk CA: bone animation (v2)
int32 numSubMotions
SkeletalSubMotion[numSubMotions]:
quat16 poseRot
quat16 bindPoseRot
quat16 poseScaleRot
quat16 bindPoseScaleRot
vec3D posePos
vec3D poseScale
vec3D bindPosePos
vec3D bindPoseScale
int32 numPosKeys
int32 numRotKeys
int32 numScaleKeys
int32 numScaleRotKeys
float fMaxError
string nodeName
// fTime of first item in each array must be 0
PosKey[numPosKeys]:
vec3d pos
float fTime
RotKey[numRotKeys]:
quat16 rot
float fTime
ScaleKey[numScaleKeys]:
vec3d scale
float fTime
ScaleRotKey[numScaleRotKeys]:
quat16 rot
float fTime