CANM Format - KCreator/Earth-Defence-Force-Documentation GitHub Wiki
All offsets are relative to the beginning of their table entries.
Since the header is always at the beginning of the file, the offsets there are also absolute file offsets.
File format largely derived from EDF5, EDF4.1 might have unknown differences.
File format description
Header: 32 (0x20) bytes
Offset | Size | Type | Description |
---|---|---|---|
0x00 | 4 | String | Always 'CANM' |
0x04 | 4 | UInt | Always 512 0x00020000 (Version?) |
0x08 | 4 | UInt | Animation Data table count |
0x0C | 4 | UInt | Animation Data table offset |
0x10 | 4 | UInt | Animation Channel Data count |
0x14 | 4 | UInt | Animation Channel Data offset |
0x18 | 4 | UInt | BoneName table count |
0x1C | 4 | UInt | BoneName table offset |
Animation Data Table: 28 (0x1C) bytes
Offset | Size | Type | Description |
---|---|---|---|
0x00 | 4 | Int | Looping (1 == true) |
0x04 | 4 | UInt | Name string offset |
0x08 | 4 | Float | Animation duration |
0x0C | 4 | Float | Time between keyframes (0x08 / (0x10 - 1)) |
0x10 | 4 | Int | Keyframes in animation |
0x14 | 4 | UInt | Bone Data Table Count |
0x18 | 4 | Int | Bone Data Table Offset |
Bone Data Table: 8 (0x08) bytes
Offset | Size | Type | Description |
---|---|---|---|
0x00 | 2 | Short | Bone Name Index |
0x02 | 2 | Short | Position Animation Channel Data Index (-1 if none) |
0x04 | 2 | Short | Rotation Animation Channel Data Index (-1 if none) (Radians!) |
0x06 | 2 | Short | Scaling Animation Channel Data Index (-1 if none) |
Animation Channel Data: 32 (0x20) bytes
Offset | Size | Type | Description |
---|---|---|---|
0x00 | 2 | Short | Has Keyframes (1 == True) |
0x02 | 2 | Short | Keyframe Count |
0x04 | 4 | Float | Base X |
0x08 | 4 | Float | Base Y |
0x0C | 4 | Float | Base Z |
0x10 | 4 | Float | Speed Multiplier X |
0x14 | 4 | Float | Speed Multiplier Y |
0x18 | 4 | Float | Speed Multiplier Z |
0x1C | 4 | Int | Keyframe Data offset (0 if 0x00 is false) |
Keyframe Data: 6 (0x06) bytes
Offset | Size | Type | Description |
---|---|---|---|
0x00 | 2 | UShort | X Offset |
0x02 | 2 | UShort | Y Offset |
0x04 | 2 | UShort | Z Offset |
Bone Name Table: 4 (0x04) bytes
Offset | Size | Type | Description |
---|---|---|---|
0x00 | 4 | UInt | start offset of string (UTF-16) |
Strings
Note that at the end of the file, all the strings are appended. With the exception of "Scene_Root" as the first entry, all these strings are sorted in alphabetical order. Bones and Animation names, all on a pile.
Calculations:
The way animations are calculated for a frame is
X = Base X + Keyframe[i].X_offset * Speed Multiplier X
Y = Base Y + Keyframe[i].Y_offset * Speed Multiplier Y
Z = Base Z + Keyframe[i].Z_offset * Speed Multiplier Z
This is repeated for every Keyframe. The same math is used for all Animation Channel Data's. For positions this will give you relative offsets and for rotations this returns Radians. Succesfull reconstruction was done through a 4x4 Matrix. Rotation X, Y, and Z, as well as Position were made into their own 4x4 Matrices, and then multiplied in the following order:
LocalTransformMatrix = Pos @ Z Rot @ Y Rot @ X Rot
This gives a local transform matrix, which can then my multiplied with it's parents global matrix, or Identity to achieve the correct bone orientation.
Example code implementation: Blender CANM Import Script
To convert any animation curve back into data for CANM, the following math takes place:
MIN = Minimum value of all keyframes in this curve
MAX = Maximum value of all keyframes in this curve
DIFF = MAX - MIN
Base = MIN
Speed = DIFF / UShort.Max
Offset = ((KeyframeValue[i] - MIN) * UShort.Max) / DIFF
Credits:
AUK - A lot of the ground work
stafern - Making the first model viewer that had working Matrices to base further work on
Smileynator - Formatting and verification