Last active
December 23, 2019 22:30
-
-
Save scurest/ec8ff29328044803da3c5f1f2337b844 to your computer and use it in GitHub Desktop.
Notes on how IQM 2 animations work
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Notes on how IQM 2 animations work: | |
(see http://sauerbraten.org/iqm/iqm.txt) | |
There is just one global animation strip for the whole file. Each iqmanim is | |
defined by picking out one subrange of frames from this global strip. | |
Every joint has one iqmpose. Every iqmpose has ten channels for the ten TRS | |
properties (Tx Ty Tz Qx Qy Qz Qw Sx Sy Sz). Every channel maps a frame | |
number to the value of that TRS property at that frame. A channel can be | |
either constant or variable. | |
The i-th channel is constant if the i-th bit of channelmask is zero | |
is channel i constant? | |
channelmask & (1 << i) == 0 | |
The value of a constant channel at every frame is channeloffset[i]. | |
Otherwise the channel is variable and there are num_frames samples stored in | |
the frames[] array that encode the value of the channel at every frame. Each | |
channel has it's own minimum and maximum value, and each sample is an | |
unsigned 16-bit integer that interpolates between this minimum and maximum. | |
The value is computed from the sample with | |
channeloffset[i] + (16-bit sample) * channelscale[i] | |
Giving the following relation to the minimum and maximum for that channel | |
channeloffset[i] = min | |
channelscale[i] = (max - min) / (2^16 - 1) | |
The frames[] array is packed in the order | |
for each frame | |
for each joint | |
for each channel | |
if this channel is variable | |
16-bit sample | |
num_framechannels is the total number of variable channels in the whole | |
file. The frames[] array therefore has total length num_framechannels * | |
num_frames. | |
Pseudo-code to evaluate the n-th animation on its f-th frame: | |
// output[j] will be the j-th joint's local-to-parent | |
// Compute frame number in the global strip | |
frame = iqmanims[n].first_frame + f | |
samples = &frames[num_framechannels * frame] | |
for j in 0..num_poses | |
pose = iqmposes[j] | |
float trs[10] | |
for i in 0..10 | |
trs[i] = pose.channeloffset[i] | |
if pose.channelmask & (1 << i) != 0 | |
trs[i] += (*samples++) * channelscale[i] | |
output[j] = trs_to_matrix( | |
translation= trs[0..3], | |
rotation= normalize(trs[3..7]), | |
scale= trs[7..10], | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment