BMTR to plain RTM

Documentation of the steps needed to restore a plain RTM file

This page is meant to supplement and clarify certain informations written on the community wiki about the transformation data stored in binarized (BMTR) RTM files, prior knowledge of that article is required.

Introduction

Similar to P3D model files, RTM animation files are also converted into a format which is designed for easier operation for the Arma 3 engine. However, unlike the MLOD -> ODOL conversion, the RTM "binarization" is reversible without data lost (not counting the floating point, and other precision error side effects of the data compression).

Differences

While the plain RTM format stores transformation matrices, describing the absolute model space transformations of each bone from the resting pose, the BMTR format instead stores transformations in quaternion-location pairs. In addition to this, these transformations are also not absolute in model space, but relative to the parent bone, which means that the data is meaningless without a known skeleton bone structure.

Conversion

Quaternions are stored as fixed precision values. Each quaternion is made up of 4 (qx, qy, qz, qw) 16-bit signed integers. To get the actual floating point values, we need to divide each number by 16384 (2^14). The location vector on the other hand is stored as 3 (x, y, z) 16-bit half precision floating point numbers. They can either be read as unsigned integers, and converted to fractional numbers with some calculations, or read directly if the employed programming language supports it.

From the quaternion and location, we can recreate the 4×4 matrix representation of the same transformation.

Since Arma 3 uses a left-handed coordinate system, the commonly known conversion formulas need to be adjusted (certain component orders changed, some signs reversed).

Once these relative transformation matrices are colculated, we need to take the skeleton structure, and multiply the matrix of each bone with the absolute matrix of its parent bone, such that the final transformation matrix equals to matrix_parent × matrix_bone.

The multiplications must be preformed on the bones in hierarchical order.

Conceptual code

The following conceptual code snippets show how to read data from a "binarized" RTM file, and transform it into a format acceptable for Blender.

import struct
import numpy as np

# Following function reads a BMTR quaternion and a location vector from
# a file-like object, and returns the right-handed matrix representation
def transform_to_matrix(file):
    qx, qz, qy, qw = struct.unpack('<4h', file.read(8))
    x, y, z = struct.unpack('<3e', file.read(6))
    
    r00 = 1 - 2*qy**2 - 2*qz**2
    r01 = 2*qx*qy - 2*qz*qw
    r02 = 2*qx*qz + 2*qy*qw
    
    r10 = 2*qx*qy + 2*qz*qw
    r11 = 1 - 2*qx**2 - 2*qz**2
    r12 = 2*qy*qz - 2*qx*qw
    
    r20 = 2*qx*qz - 2*qy*qw
    r21 = 2*qy*qz + 2*qx*qw
    r22 = 1 - 2*qx**2 - 2*qy**2
    
    matrix = [
        [r00, r01, -r02, x],
        [r10, r11, -r12, y],
        [-r20, -r21, r22, z],
        [0, 0, 0, 1]
    ]

    return output

# We can collect the transformation matrices in a dictionary, where the key
# is the bone the transformation belongs to, and the value is the matrix
# With a second dictionary having the bone-parent pairs in hierarchical
# order, the RTM-like absolute matrices can be calculated
def calculate_absolute_matrices():
    matrices = {
        "base_bone": ..., # matrix
        "bone2": ..., # matrix
        "bone1": ... # matrix
    }
    bone_hierarchy = {
        "base_bone": "",
        "bone1": "base_bone",
        "bone2": "base_bone"
    }
    
    default = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
    for bone in bone_hierarchy:
        mat_bone = np.matrix(matrices[bone])
        mat_parent = np.matrix(matrices.get(bone_hierarchy[bone], default))
        
        matrices[bone] = (mat_parent @ mat_bone).tolist()
    
    return matrices

Last updated