Skip to content

ANIMATION

Jocelyn Beedie edited this page Oct 9, 2021 · 8 revisions

ANIMATION

This is the very first file type to be structured using decompilation!

struct Animation {
    float length;
    uint16_t unk;
    uint32_t numAnimationMeshes;
    AnimationMesh animationMeshes[numAnimationMeshes];
    uint32_t numAnimationNodes;
    AnimationNode animationNodes[numAnimationNodes];
    uint32_t numAnimationMaterials;
    AnimationMaterial animationMaterials[numAnimationMaterials];
}
struct AnimationMesh {
    int32_t name;
    uint16_t unk;
    KeyFramerFloat track;
}
struct AnimationNode {
    int32_t name;
    uint16_t unk;
    KeyFramerVec3fComp track1;
    KeyFramerRot track2;
    KeyFramerVec3fComp track3;
    KeyFramerMessage track4;
}
struct AnimationMaterial {
    int32_t name;
    uint16_t unk;
    KeyFramerFloat track1;
    KeyFramerFloat track2;
    KeyFramerFloat track3;
    KeyFramerFloat track4;
    KeyFramerFloat track5;
    KeyFramerVec3fComp track6;
    KeyFramerVec3fComp track7;
    KeyFramerFloat track8;
}

KeyframerFloat

struct KeyFramerFloat {
    uint16_t interp;
    uint32_t num_frames;
    struct KeyFloat {
        uint16_t frame;
        uint16_t junk;
        Vector3 data;
    }
    KeyFloat frames[num_frames];
}

KeyframerVec3fComp

struct KeyFramerVec3fComp {
    uint16_t interp;
    uint32_t num_frames;
    struct KeyVec3fComp {
        uint16_t frame;
        int16_t pos[3];
        int16_t handle_left[3];
        int16_t handle_right[3];
    }
    KeyVec3fComp frames[num_frames];
}

To convert a point in KeyVec3fComp to a Vector3, just convert each component to a float and multiply by 0.001. For example:

KeyVec3fComp v;
Vector3 pos = Vector3(v.pos[0], v.pos[1], v.pos[2]) * 0.001;

KeyframerRot

struct KeyFramerRot {
    uint16_t interp;
    uint32_t num_frames;
    struct KeyRot {
        int16_t frame;
        int16_t quat[4];
        iint16_t junk;
        float omega;
        float sinom;
    }
    KeyRot frames[num_frames];
}

Converting to a quaternion

float convertQuatComponent(int16_t value) {
    // keep only 12 bits of `value`, that way it stays close to the range [-1, 1]
    return (float)(val & 0x0FFF) * 0.0005;
}

Quatf convertQuat(KeyRot key) {
    return Quatf (
        convertQuatComponent(key.quat[0]),
        convertQuatComponent(key.quat[1]),
        convertQuatComponent(key.quat[2]),
        convertQuatComponent(key.quat[3]),
    );
}

Interpolating Quaternions is fairly straightforward, and any old slerp function should work fine. However, the game also includes precomputed omega/sinom values for the slerp function, for a small performance boost. For completion's sake, here is (roughly) how the game interpolates quaternions:

Quatf interp(KeyRot from, KeyRot to, float t) {
  if (from.omega == 0.0) {
    return convertQuat(from);
  }
  else {
    Quat fromq = convertQuat(from);
    Quat toq = convertQuat(to);
    if (from.omega > 2 * PI) {
      from.omega = from.omega - 2 * PI;
      toq = -toq;
    }
    float t1 = sin(t * from.omega);
    float t2 = sin((1.0 - t) * from.omega);
    return from.sinom * (from1 * t1 + toq * t2);
  }
}

KeyframerMessage

struct KeyFramerMessage {
    uint16_t interp;
    uint32_t num_frames;
    struct Message {
        uint32_t unk1;
        uint32_t unk2;
        // if unk1 in [0x11, 0x12, 0x13]:
            uint32_t strlen; // will create a buffer overflow if >= 144
            char data[strlen];
        int32_t name;
    }
    struct KeyMessage {
        uint16_t frame;
        uint16_t junk;
        uint32_t num_messages;
        Message messages[num_messages];
    }
    KeyMessage frames[num_frames];
}

Clone this wiki locally