Skip to content

Instantly share code, notes, and snippets.

@jackoalan
Created May 2, 2015 19:28
Show Gist options
  • Save jackoalan/72a62f320dbebb52c66d to your computer and use it in GitHub Desktop.
Save jackoalan/72a62f320dbebb52c66d to your computer and use it in GitHub Desktop.
MP1 ANIM-format extractor
/*
* ANIM.c
* rwk
*
* Created on 10/18/13.
*
*/
#include <stdio.h>
#include <math.h>
#include <float.h>
#include "ANIM.h"
/* Intermediate key binary decoder
* Reads binary keyframe data, populates Blender action,
* deletes file when complete */
static const char* ANIM_BIN_DEC =
" def anim_add_kf(fcurve_idx, interp, time, val):\n"
" crv = act.fcurves[fcurve_idx]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = interp\n"
" crv.keyframe_points[-1].co = (time, val)\n"
" \n"
" def anim_bin_dec(file_path):\n"
" with open(file_path, 'rb') as f:\n"
" while True:\n"
" data = f.read(24)\n"
" if len(data) == 24:\n"
" m = struct.unpack('IIiIIf', data)\n"
" if m[1] == 0:\n"
" interp = 'CONSTANT'\n"
" elif m[1] == 1:\n"
" interp = 'LINEAR'\n"
" if m[2] < 0:\n"
" val = m[5]\n"
" else:\n"
" val = m[5] - bone_trans_heads[m[2]][m[3]]\n"
" anim_add_kf(m[0], interp, m[4], val)\n"
" else:\n"
" break\n"
" os.unlink(file_path)\n";
struct ANIM_JointHeader {
u16 startCode;
u16 jointId;
u16 keyCount;
s16 alpha_rot;
char alpha_rot_sz;
s16 beta_rot;
char beta_rot_sz;
s16 gamma_rot;
char gamma_rot_sz;
u16 nextCode;
s16 x_loc;
char x_loc_sz;
s16 y_loc;
char y_loc_sz;
s16 z_loc;
char z_loc_sz;
s32 rot_accum[3];
s32 trans_accum[3];
};
static u32 pick_out_bit(u8* bs, u32 cur) {
/* I suspect this stream to actually be delta-encoded */
/* There is a sign bit and the remaining bits incrementally */
/* adjust the default value provided in the joint header */
/* EDIT: Turns out the bitstream is indeed variable-length */
/* differential values with two's compliment signing */
u32 byteCur = (cur / 32) * 4;
u32 bitRem = cur % 32;
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
u32 tempBuf = *(u32*)(bs + byteCur);
tempBuf = swap_u32(tempBuf);
tempBuf = tempBuf >> bitRem;
/* mask it (excluding sign bit) */
u32 mask = 1;
tempBuf &= mask;
return tempBuf;
}
static s32 pick_out_bits(u8* bs, u32 cur, u8 bitLen) {
/* I suspect this stream to actually be delta-encoded */
/* There is a sign bit and the remaining bits incrementally */
/* adjust the default value provided in the joint header */
/* EDIT: Turns out the bitstream is indeed variable-length */
/* differential values with two's compliment signing */
if (!bitLen)
return 0;
/* If there's one bit, simply return pickOutBit debug value */
if (bitLen <= 1) {
if (pick_out_bit(bs, cur))
return 10000000;
else
return 0;
}
u32 byteCur = (cur / 32) * 4;
u32 bitRem = cur % 32;
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
u32 tempBuf = *(u32*)(bs + byteCur);
tempBuf = swap_u32(tempBuf);
tempBuf = tempBuf >> bitRem;
/* If this shift underflows the value, buffer the next 32 bits */
/* And tack onto shifted buffer */
if((bitRem + bitLen) > 32)
{
u32 tempBuf2 = *(u32*)(bs + byteCur + 4);
tempBuf2 = swap_u32(tempBuf2);
tempBuf |= (tempBuf2 << (32 - bitRem));
}
/* Pick out sign */
u32 sign = tempBuf >> (bitLen - 1);
sign &= 0x1;
/* If signed, invert the bastard (Two's compliment) */
if(sign)
tempBuf = ~tempBuf;
/* mask it (excluding sign bit) */
u32 mask = (1 << (bitLen - 1)) - 1;
tempBuf &= mask;
/* Return delta value */
return (s32)(sign ? (tempBuf + 1) * -1 : tempBuf);
}
static void make_jh_little(struct ANIM_JointHeader* jh) {
jh->startCode = swap_s16(jh->startCode);
jh->jointId = swap_s16(jh->jointId);
jh->keyCount = swap_s16(jh->keyCount);
jh->alpha_rot = swap_s16(jh->alpha_rot);
jh->beta_rot = swap_s16(jh->beta_rot);
jh->gamma_rot = swap_s16(jh->gamma_rot);
jh->nextCode = swap_s16(jh->nextCode);
if (jh->nextCode) {
jh->x_loc = swap_s16(jh->x_loc);
jh->y_loc = swap_s16(jh->y_loc);
jh->z_loc = swap_s16(jh->z_loc);
}
}
static u32 get_kf_count(u32* kfBitmask, u32 potkfCount) {
u32 curKfBit = 0;
u32 curKfCnt = 0;
while (curKfBit < potkfCount) {
u32 curKfBool = kfBitmask[curKfBit / 32];
curKfBool = curKfBool >> (curKfBit % 32);
curKfBool &= 0x1;
if(curKfBool)
++curKfCnt;
++curKfBit;
}
return curKfCnt;
}
static u32 get_kf_frame(u32 kfIdx, u32* kfBitmask, u32 potkfCount) {
u32 curKfBit = 0;
s32 curKfIdx = -1;
while(curKfBit < potkfCount) {
u32 curKfBool = kfBitmask[curKfBit / 32];
curKfBool = curKfBool >> (curKfBit % 32);
curKfBool &= 0x1;
if(curKfBool)
{
++curKfIdx;
if(curKfIdx == kfIdx)
return curKfBit;
}
++curKfBit;
}
/* Ideally shouldn't be reached, but safe value */
return 0;
}
struct ANIM_JointUpdate
{
u32 jointId;
s32 alpha_rot;
s32 beta_rot;
s32 gamma_rot;
u8 updTransBool;
s32 x_loc;
s32 y_loc;
s32 z_loc;
u8 unBit;
};
struct ANIM_JointUpdateFloat
{
u32 jointId;
float alpha_rot;
float beta_rot;
float gamma_rot;
u8 updTransBool;
float x_loc;
float y_loc;
float z_loc;
u8 unBit;
};
struct ANIM_Keyframe
{
u32 potentialKeyframe;
u32 firstJointUpdateIdx;
u32 jointUpdateCount;
};
struct ANIM_Header_Fmt0 {
float duration;
u32 val0;
float interval;
u32 val1, keyframe_count, val3;
u32 dict_length;
};
struct ANIM_Header_Fmt2 {
u32 key_count, event_ref, val3;
float duration, interval;
u32 val4, val5;
s32 rotation_divisor;
float translation_multiplier;
u32 bone_count;
u32 val6;
u32 kf_bitfield_len; /* (In Bits) */
};
/* Rotation order enumerations */
#define QUAT 0
#define XYZ 1
#define XZY 2
#define YXZ 3
#define YZX 4
#define ZXY 5
#define ZYX 6
void ANIM_decode(struct pak_entry* entry, FILE* py_out, const char* path_prefix, unsigned action_idx) {
/* Get Data */
u8* anim_buf = NULL;
pak_lookup_get_data(entry, &anim_buf);
u8* anim_cur = anim_buf;
/* Format Type */
u32 anim_fmt = swap_u32(*(u32*)anim_cur);
anim_cur += 4;
if (anim_fmt == 2) {
# pragma mark Bitstream-Differential-Compressed Euler-Vector Format
struct ANIM_Header_Fmt2* header = (struct ANIM_Header_Fmt2*)anim_cur;
/* Swap header information */
header->key_count = swap_u32(header->key_count);
header->val3 = swap_u32(header->val3);
header->val4 = swap_u32(header->val4);
header->val5 = swap_u32(header->val5);
header->val6 = swap_u32(header->val6);
header->duration = swap_float(header->duration);
header->interval = swap_float(header->interval);
header->rotation_divisor = swap_u32(header->rotation_divisor);
header->translation_multiplier = swap_float(header->translation_multiplier);
header->bone_count = swap_u32(header->bone_count);
header->kf_bitfield_len = swap_u32(header->kf_bitfield_len);
fprintf(py_out,
" bone_trans_heads = []\n"
" act.rwk_fps = round(%f)\n"
" act.rwk_anim_props = '0x%X 0x%X %u %u %u %u'\n", (1.0f / header->interval),
header->key_count, header->rotation_divisor, header->val3, header->val4, header->val5, header->val6);
/* Read in keyframe bitmask */
u32 bitfield_byte_size = header->kf_bitfield_len / 32;
if(header->kf_bitfield_len % 32)
++bitfield_byte_size;
bitfield_byte_size *= 4;
u32* kf_bitfield = (u32*)(anim_buf + 52);
/* Make bitmask little */
int i;
for(i=0 ; i<(header->kf_bitfield_len/32)+1 ; ++i)
kf_bitfield[i] = swap_u32(kf_bitfield[i]);
/* Advance to end of bitmask */
anim_cur = anim_buf + 52 + bitfield_byte_size;
/* Advance past two joint counts */
anim_cur += 8;
/* Allocate temporary buffer to hold joint headers */
struct ANIM_JointHeader* jh_arr = alloca(sizeof(struct ANIM_JointHeader) * header->bone_count);
memset(jh_arr, 0, sizeof(struct ANIM_JointHeader) * header->bone_count);
/* Populate Joint headers */
u64 kf_size = 0;
for (i=0 ; i<header->bone_count ; ++i) {
u8* file_jh = anim_cur;
struct ANIM_JointHeader* jh = &jh_arr[i];
jh->startCode = *(u16*)(file_jh);
jh->jointId = *(u16*)(file_jh + 2);
jh->keyCount = *(u16*)(file_jh + 4);
jh->alpha_rot = *(u16*)(file_jh + 6);
jh->alpha_rot_sz = *(char*)(file_jh + 8);
jh->beta_rot = *(u16*)(file_jh + 9);
jh->beta_rot_sz = *(char*)(file_jh + 11);
jh->gamma_rot = *(u16*)(file_jh + 12);
jh->gamma_rot_sz = *(char*)(file_jh + 14);
jh->nextCode = *(u16*)(file_jh + 15);
kf_size += 1;
kf_size += jh->alpha_rot_sz;
kf_size += jh->beta_rot_sz;
kf_size += jh->gamma_rot_sz;
if (jh->nextCode) {
jh->x_loc = *(u16*)(file_jh + 17);
jh->x_loc_sz = *(char*)(file_jh + 19);
jh->y_loc = *(u16*)(file_jh + 20);
jh->y_loc_sz = *(char*)(file_jh + 22);
jh->z_loc = *(u16*)(file_jh + 23);
jh->z_loc_sz = *(char*)(file_jh + 25);
kf_size += jh->x_loc_sz;
kf_size += jh->y_loc_sz;
kf_size += jh->z_loc_sz;
}
make_jh_little(jh);
jh->rot_accum[0] = jh->alpha_rot;
jh->rot_accum[1] = jh->beta_rot;
jh->rot_accum[2] = jh->gamma_rot;
jh->trans_accum[0] = jh->x_loc;
jh->trans_accum[1] = jh->y_loc;
jh->trans_accum[2] = jh->z_loc;
fprintf(py_out,
" bone_id = %u\n"
" if bone_id in arm_string_table:\n"
" bone_string = arm_string_table[bone_id]\n"
" else:\n"
" bone_string = 'bone_%%u' %% bone_id\n"
" action_group = act.groups.new(bone_string)\n"
" \n"
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=0, action_group=bone_string)\n"
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=1, action_group=bone_string)\n"
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=2, action_group=bone_string)\n"
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=3, action_group=bone_string)\n"
" \n",
jh->jointId);
if (jh->nextCode) {
fprintf(py_out,
" bone_parent_head = (0.0,0.0,0.0)\n"
" if arm_obj.data.bones[bone_string].parent is not None:\n"
" bone_parent_head = Vector(arm_obj.data.bones[bone_string].head_local) - Vector(arm_obj.data.bones[bone_string].parent.head_local)\n"
" bone_trans_heads.append(bone_parent_head)\n"
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=0, action_group=bone_string)\n"
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=1, action_group=bone_string)\n"
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].location', index=2, action_group=bone_string)\n"
" \n");
} else {
fprintf(py_out,
" bone_trans_heads.append((0,0,0))\n");
}
fprintf(py_out,
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_mode', action_group=bone_string)\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].co = (0, 0)\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" \n");
if (jh->nextCode)
anim_cur += 26;
else
anim_cur += 17;
}
/* First, allocate space for keyframes */
unsigned keyframe_count = get_kf_count(kf_bitfield, header->kf_bitfield_len);
/* Utility to add keyframe to fcurve (with less repeated code) */
fprintf(py_out, "%s\n", ANIM_BIN_DEC);
/* Binary file to hold intermediate keys */
char anim_bin_path[MAXPATHLEN];
snprintf(anim_bin_path, MAXPATHLEN, "%s/actors/ANIM_%08X_%u.bin", path_prefix, swap_u32(entry->uid), action_idx);
FILE* anim_bin = fopen(anim_bin_path, "wb");
struct bin_key {
u32 crv_idx;
u32 interp;
s32 translation;
u32 array_idx;
u32 frame;
float val;
} bin_key;
fprintf(py_out, " anim_bin_dec('ANIM_%08X_%u.bin')\n\n", swap_u32(entry->uid), action_idx);
/* First pass, generate FCurve */
u32 bitCursor = 0;
void* bitstream = anim_cur;
for (i=0 ; i<keyframe_count ; ++i) {
/* Count of frames into action */
u32 time_stamp = get_kf_frame(i, kf_bitfield, header->kf_bitfield_len);
unsigned fcurve_idx = 0;
int j;
for (j=0 ; j<header->bone_count ; ++j) {
struct ANIM_JointHeader* jh = &jh_arr[j];
/* BEGIN JOINT-UPDATE EXTRACTION */
s32 rot_raw[3] = {0,0,0};
bool flip_bit = false;
if (i) {
/* Advance bit cursor past one bit per update */
flip_bit = pick_out_bit(bitstream, bitCursor) ? true : false;
bitCursor += 1;
/* This rotation delta */
rot_raw[0] = pick_out_bits(bitstream, bitCursor, jh->alpha_rot_sz);
bitCursor += jh->alpha_rot_sz;
rot_raw[1] = pick_out_bits(bitstream, bitCursor, jh->beta_rot_sz);
bitCursor += jh->beta_rot_sz;
rot_raw[2] = pick_out_bits(bitstream, bitCursor, jh->gamma_rot_sz);
bitCursor += jh->gamma_rot_sz;
jh->rot_accum[0] += rot_raw[0];
jh->rot_accum[1] += rot_raw[1];
jh->rot_accum[2] += rot_raw[2];
/* This translation delta */
if (jh->nextCode) {
s32 trans_raw[3];
trans_raw[0] = pick_out_bits(bitstream, bitCursor, jh->x_loc_sz);
bitCursor += jh->x_loc_sz;
trans_raw[1] = pick_out_bits(bitstream, bitCursor, jh->y_loc_sz);
bitCursor += jh->y_loc_sz;
trans_raw[2] = pick_out_bits(bitstream, bitCursor, jh->z_loc_sz);
bitCursor += jh->z_loc_sz;
jh->trans_accum[0] += trans_raw[0];
jh->trans_accum[1] += trans_raw[1];
jh->trans_accum[2] += trans_raw[2];
}
}
float quantization_factor = M_PI_2 / header->rotation_divisor;
rwk_vector euler_vector = {
sinf(jh->rot_accum[0] * quantization_factor),
sinf(jh->rot_accum[1] * quantization_factor),
sinf(jh->rot_accum[2] * quantization_factor)
};
float w =
euler_vector.v[0] * euler_vector.v[0] +
euler_vector.v[1] * euler_vector.v[1] +
euler_vector.v[2] * euler_vector.v[2];
w = 1.0 - w;
w = MAX(w, 0.0);
w = sqrtf(w);
rwk_vector final_q = {
flip_bit ? -w : w,
euler_vector.v[0],
euler_vector.v[1],
euler_vector.v[2],
};
bin_key.crv_idx = fcurve_idx++;
bin_key.interp = 1;
bin_key.translation = -1;
bin_key.frame = time_stamp;
bin_key.val = final_q.v[0];
fwrite(&bin_key, 1, sizeof(bin_key), anim_bin);
bin_key.crv_idx = fcurve_idx++;
bin_key.val = final_q.v[1];
fwrite(&bin_key, 1, sizeof(bin_key), anim_bin);
bin_key.crv_idx = fcurve_idx++;
bin_key.val = final_q.v[2];
fwrite(&bin_key, 1, sizeof(bin_key), anim_bin);
bin_key.crv_idx = fcurve_idx++;
bin_key.val = final_q.v[3];
fwrite(&bin_key, 1, sizeof(bin_key), anim_bin);
/* Translation */
if (jh->nextCode) {
bin_key.crv_idx = fcurve_idx++;
bin_key.translation = j;
bin_key.array_idx = 0;
bin_key.val = jh->trans_accum[0] * header->translation_multiplier;
fwrite(&bin_key, 1, sizeof(bin_key), anim_bin);
bin_key.crv_idx = fcurve_idx++;
bin_key.array_idx = 1;
bin_key.val = jh->trans_accum[1] * header->translation_multiplier;
fwrite(&bin_key, 1, sizeof(bin_key), anim_bin);
bin_key.crv_idx = fcurve_idx++;
bin_key.array_idx = 2;
bin_key.val = jh->trans_accum[2] * header->translation_multiplier;
fwrite(&bin_key, 1, sizeof(bin_key), anim_bin);
}
++fcurve_idx;
}
if (i==0)
bitCursor = 0;
}
fclose(anim_bin);
} else if (anim_fmt == 0) {
# pragma mark Quaternion Array Format
/* 4-Component Vector Array (Quaternion Rotations, Optional Translation mappings) */
struct ANIM_Header_Fmt0* header = (struct ANIM_Header_Fmt0*)anim_cur;
int i,j;
/* Swap header information */
header->duration = swap_float(header->duration);
header->interval = swap_float(header->interval);
header->dict_length = swap_u32(header->dict_length);
header->keyframe_count = swap_u32(header->keyframe_count);
fprintf(py_out,
" act.rwk_fps = round(%f)\n"
" anim_bone_table = {}\n",
(1.0f / header->interval));
anim_cur += sizeof(struct ANIM_Header_Fmt0);
u8* bone_dict = anim_cur;
for (i=0 ; i<header->dict_length ; ++i) {
if (bone_dict[i] == 0xff)
continue;
fprintf(py_out,
" bone_id = %u\n"
" if bone_id in arm_string_table:\n"
" bone_string = arm_string_table[bone_id]\n"
" else:\n"
" bone_string = 'bone_%%u' %% bone_id\n"
" action_group = act.groups.new(bone_string)\n"
" anim_bone_table[%u] = action_group\n"
" \n"
" act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=0, action_group=bone_string)\n"
" act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=1, action_group=bone_string)\n"
" act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=2, action_group=bone_string)\n"
" act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_quaternion', index=3, action_group=bone_string)\n",
i, bone_dict[i]);
fprintf(py_out,
" crv = act.fcurves.new('pose.bones[\"'+bone_string+'\"].rotation_mode', action_group=bone_string)\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].co = (0, 0)\n"
" \n");
}
anim_cur += header->dict_length;
/* Bone Mode Table (for enabling translation) */
u32 bone_count = swap_u32(*(u32*)anim_cur);
anim_cur += 4;
u8* bone_modes = anim_cur;
for (i=0 ; i<bone_count ; ++i) {
if (bone_modes[i] == 0xff)
continue;
fprintf(py_out,
" action_group = anim_bone_table[%u]\n"
" \n"
" act.fcurves.new('pose.bones[\"'+action_group.name+'\"].location', index=0, action_group=action_group.name)\n"
" act.fcurves.new('pose.bones[\"'+action_group.name+'\"].location', index=1, action_group=action_group.name)\n"
" act.fcurves.new('pose.bones[\"'+action_group.name+'\"].location', index=2, action_group=action_group.name)\n",
bone_modes[i]);
}
anim_cur += bone_count;
/* Joint Update Table */
u32 joint_update_count = swap_u32(*(u32*)anim_cur);
anim_cur += 4;
float* joint_update_table = (float*)anim_cur;
for (i=0 ; i<bone_count ; ++i) {
fprintf(py_out,
" action_group = anim_bone_table[%u]\n", i);
for (j=0 ; j<header->keyframe_count ; ++j) {
fprintf(py_out,
" crv = action_group.channels[0]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" crv.keyframe_points[-1].co = (%u, %f)\n",
j, swap_float(joint_update_table[(i * header->keyframe_count + j) * 4 + 0]));
fprintf(py_out,
" crv = action_group.channels[1]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" crv.keyframe_points[-1].co = (%u, %f)\n",
j, swap_float(joint_update_table[(i * header->keyframe_count + j) * 4 + 1]));
fprintf(py_out,
" crv = action_group.channels[2]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" crv.keyframe_points[-1].co = (%u, %f)\n",
j, swap_float(joint_update_table[(i * header->keyframe_count + j) * 4 + 2]));
fprintf(py_out,
" crv = action_group.channels[3]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" crv.keyframe_points[-1].co = (%u, %f)\n",
j, swap_float(joint_update_table[(i * header->keyframe_count + j) * 4 + 3]));
}
}
anim_cur += 16 * joint_update_count;
/* Triple Table (Positions) */
u32 triple_count = swap_u32(*(u32*)anim_cur);
anim_cur += 4;
float* triple_table = (float*)anim_cur;
for (i=0 ; i<bone_count ; ++i) {
if (bone_modes[i] == 0xff)
continue;
fprintf(py_out,
" action_group = anim_bone_table[%u]\n", bone_modes[i]);
for (j=0 ; j<header->keyframe_count ; ++j) {
fprintf(py_out,
" crv = action_group.channels[5]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" crv.keyframe_points[-1].co = (%u, %f)\n",
j, swap_float(triple_table[(i * header->keyframe_count + j) * 3 + 0]));
fprintf(py_out,
" crv = action_group.channels[6]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" crv.keyframe_points[-1].co = (%u, %f)\n",
j, swap_float(triple_table[(i * header->keyframe_count + j) * 3 + 1]));
fprintf(py_out,
" crv = action_group.channels[7]\n"
" crv.keyframe_points.add()\n"
" crv.keyframe_points[-1].interpolation = 'LINEAR'\n"
" crv.keyframe_points[-1].co = (%u, %f)\n",
j, swap_float(triple_table[(i * header->keyframe_count + j) * 3 + 2]));
}
}
anim_cur += 12 * triple_count;
/* EVNT Ref */
/*u32 evnt_ref = *(u32*)anim_cur;*/
} else {
printf("Unknown ANIM format\n");
}
free(anim_buf);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment