mirror of
https://gitlab.com/ryandwyer/perfect-dark
synced 2026-07-08 21:14:40 -04:00
784 lines
20 KiB
C
784 lines
20 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/prop.h"
|
|
#include "game/game_1531a0.h"
|
|
#include "game/bg.h"
|
|
#include "bss.h"
|
|
#include "lib/dma.h"
|
|
#include "lib/memp.h"
|
|
#include "lib/mtx.h"
|
|
#include "lib/anim.h"
|
|
#include "lib/lib_2f490.h"
|
|
#include "lib/libc/ll.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
#define ANIM_HEADER_CACHE_SIZE 40
|
|
#define ANIM_FRAME_CACHE_SIZE 32
|
|
|
|
u8 *g_AnimFrameByteSlots;
|
|
u8 **g_AnimFrameBytes;
|
|
s16 *g_AnimFrameAnimNums;
|
|
s16 *g_AnimFrameFrameNums;
|
|
u8 *g_AnimFrameBirths;
|
|
u8 *g_AnimHeaderByteSlots;
|
|
u8 **g_AnimHeaderBytes;
|
|
s16 *g_AnimHeaderAnimNums;
|
|
s32 *g_AnimHeaderBirths;
|
|
s16 g_NumRomAnimations;
|
|
struct animtableentry *g_RomAnims;
|
|
|
|
u32 g_NextAnimFrameIndex = 0;
|
|
s32 g_NextAnimHeaderIndex = 0;
|
|
s16 g_NumAnimations = 0;
|
|
struct animtableentry *g_Anims = NULL;
|
|
u8 *g_AnimToHeaderSlot = NULL;
|
|
s16 *g_AnimAverageMoveDist = NULL;
|
|
s32 g_AnimMaxBytesPerFrame = 176;
|
|
s32 g_AnimMaxHeaderLength = 608;
|
|
bool g_AnimHostEnabled = false;
|
|
u8 *g_AnimHostSegment = NULL;
|
|
|
|
extern u8 _animationsTableRomStart;
|
|
extern u8 _animationsTableRomEnd;
|
|
|
|
void anims_init(void)
|
|
{
|
|
s32 i;
|
|
u32 *ptr;
|
|
u32 tablelen = ALIGN64(&_animationsTableRomEnd - &_animationsTableRomStart);
|
|
|
|
ptr = memp_alloc(tablelen, MEMPOOL_PERMANENT);
|
|
dma_exec(ptr, (romptr_t) &_animationsTableRomStart, tablelen);
|
|
|
|
g_NumAnimations = g_NumRomAnimations = ptr[0];
|
|
g_Anims = g_RomAnims = (struct animtableentry *)&ptr[1];
|
|
|
|
g_AnimMaxHeaderLength = 1;
|
|
g_AnimMaxBytesPerFrame = 1;
|
|
|
|
for (i = 0; i < g_NumAnimations; i++) {
|
|
if (g_Anims[i].headerlen > g_AnimMaxHeaderLength) {
|
|
g_AnimMaxHeaderLength = g_Anims[i].headerlen;
|
|
}
|
|
|
|
if (g_Anims[i].bytesperframe > g_AnimMaxBytesPerFrame) {
|
|
g_AnimMaxBytesPerFrame = g_Anims[i].bytesperframe;
|
|
}
|
|
}
|
|
|
|
g_AnimMaxHeaderLength = ALIGN16(g_AnimMaxHeaderLength + 34);
|
|
g_AnimMaxBytesPerFrame = ALIGN16(g_AnimMaxBytesPerFrame + 34);
|
|
|
|
g_AnimToHeaderSlot = memp_alloc(ALIGN64(g_NumAnimations), MEMPOOL_PERMANENT);
|
|
g_AnimAverageMoveDist = memp_alloc(ALIGN64(g_NumAnimations * sizeof(*g_AnimAverageMoveDist)), MEMPOOL_PERMANENT);
|
|
g_AnimFrameByteSlots = memp_alloc(ALIGN64(ANIM_FRAME_CACHE_SIZE * g_AnimMaxBytesPerFrame), MEMPOOL_PERMANENT);
|
|
g_AnimFrameBytes = memp_alloc(ALIGN64(ANIM_FRAME_CACHE_SIZE * sizeof(*g_AnimFrameBytes)), MEMPOOL_PERMANENT);
|
|
g_AnimFrameAnimNums = memp_alloc(ALIGN64(ANIM_FRAME_CACHE_SIZE * sizeof(*g_AnimFrameAnimNums)), MEMPOOL_PERMANENT);
|
|
g_AnimFrameFrameNums = memp_alloc(ALIGN64(ANIM_FRAME_CACHE_SIZE * sizeof(*g_AnimFrameFrameNums)), MEMPOOL_PERMANENT);
|
|
g_AnimFrameBirths = memp_alloc(ALIGN64(ANIM_FRAME_CACHE_SIZE * sizeof(*g_AnimFrameBirths)), MEMPOOL_PERMANENT);
|
|
g_AnimHeaderByteSlots = memp_alloc(ALIGN64(ANIM_HEADER_CACHE_SIZE * g_AnimMaxHeaderLength), MEMPOOL_PERMANENT);
|
|
g_AnimHeaderBytes = memp_alloc(ALIGN64(ANIM_HEADER_CACHE_SIZE * sizeof(*g_AnimHeaderBytes)), MEMPOOL_PERMANENT);
|
|
g_AnimHeaderAnimNums = memp_alloc(ALIGN64(ANIM_HEADER_CACHE_SIZE * sizeof(*g_AnimHeaderAnimNums)), MEMPOOL_PERMANENT);
|
|
g_AnimHeaderBirths = memp_alloc(ALIGN64(ANIM_HEADER_CACHE_SIZE * sizeof(*g_AnimHeaderBirths)), MEMPOOL_PERMANENT);
|
|
|
|
anims_init_tables();
|
|
|
|
g_AnimHostSegment = NULL;
|
|
g_AnimHostEnabled = false;
|
|
}
|
|
|
|
void anims_init_tables(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_NumAnimations; i++) {
|
|
g_AnimToHeaderSlot[i] = 0xff;
|
|
g_AnimAverageMoveDist[i] = 0;
|
|
}
|
|
|
|
for (i = 0; i < ANIM_FRAME_CACHE_SIZE; i++) {
|
|
g_AnimFrameAnimNums[i] = 0;
|
|
g_AnimFrameFrameNums[i] = 0;
|
|
g_AnimFrameBirths[i] = 0;
|
|
}
|
|
|
|
for (i = 0; i < ANIM_HEADER_CACHE_SIZE; i++) {
|
|
g_AnimHeaderAnimNums[i] = 0;
|
|
g_AnimHeaderBirths[i] = -2;
|
|
}
|
|
}
|
|
|
|
void anims_reset(void)
|
|
{
|
|
g_NumAnimations = g_NumRomAnimations;
|
|
g_Anims = g_RomAnims;
|
|
g_AnimHostEnabled = false;
|
|
}
|
|
|
|
s32 anim_get_num_frames(s16 animnum)
|
|
{
|
|
return g_Anims[animnum].numframes;
|
|
}
|
|
|
|
bool anim_has_frames(s16 animnum)
|
|
{
|
|
return animnum < g_NumAnimations && g_Anims[animnum].numframes > 0;
|
|
}
|
|
|
|
s32 anim_get_num_animations(void)
|
|
{
|
|
return g_NumAnimations;
|
|
}
|
|
|
|
extern u8 _animationsSegmentRomStart;
|
|
|
|
u8 *anim_dma(u8 *dst, u32 segoffset, u32 len)
|
|
{
|
|
if (g_AnimHostEnabled) {
|
|
bcopy(&g_AnimHostSegment[segoffset], dst, len);
|
|
return dst;
|
|
}
|
|
|
|
return dma_exec_with_auto_align(dst, (romptr_t) &_animationsSegmentRomStart + segoffset, len);
|
|
}
|
|
|
|
/**
|
|
* Return -1 if the given apparent frame is a repeat frame, or if not a repeat
|
|
* frame then remap the apparent frame to a real one and return it.
|
|
*
|
|
* The end of the header can contain a sequence of shorts such as:
|
|
* -1, 55, 30
|
|
*
|
|
* The values are iterated backwards in pairs of 2 and are terminated by -1.
|
|
*
|
|
* In each pair, the right value is the repeatfromframe and the left value is
|
|
* the repeattoframe. In the above example, apparent frames 30 to 55 are
|
|
* repeated, so the remapping looks like:
|
|
* 29 -> 29
|
|
* 30 -> -1
|
|
* ...
|
|
* 55 -> -1
|
|
* 56 -> 30
|
|
* 57 -> 31
|
|
*/
|
|
s32 anim_get_remapped_frame(s16 animnum, s32 apparentframe)
|
|
{
|
|
u8 *ptr = (u8 *)(g_AnimHeaderBytes[g_AnimToHeaderSlot[animnum]] + g_Anims[animnum].headerlen - 2);
|
|
s32 realframe = apparentframe;
|
|
|
|
while (true) {
|
|
s16 repeatfromframe = ptr[0] << 8 | ptr[1];
|
|
s16 repeattoframe;
|
|
|
|
if (repeatfromframe < 0) {
|
|
break;
|
|
}
|
|
|
|
repeattoframe = ptr[-2] << 8 | ptr[-1];
|
|
ptr -= 4;
|
|
|
|
if (repeatfromframe <= apparentframe) {
|
|
if (repeattoframe < apparentframe) {
|
|
realframe = realframe - repeattoframe + repeatfromframe - 1;
|
|
} else {
|
|
realframe = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return realframe;
|
|
}
|
|
|
|
/**
|
|
* Similar to the above, but with the following differences:
|
|
* - Write the remapped frame to the frameptr pointer instead of returning it.
|
|
* - If the apparent frame is a repeat, write the original frame rather than -1.
|
|
* - Return true if the frame is original or false if it's a repeat.
|
|
*/
|
|
bool anim_remap_frame_for_load(s16 animnum, s32 apparentframe, s32 *frameptr)
|
|
{
|
|
u8 *ptr = (u8 *)(g_AnimHeaderBytes[g_AnimToHeaderSlot[animnum]] + g_Anims[animnum].headerlen - 2);
|
|
s32 result = apparentframe;
|
|
bool ret = true;
|
|
|
|
while (true) {
|
|
s16 repeatfromframe = ptr[0] << 8 | ptr[1];
|
|
s16 repeattoframe;
|
|
|
|
if (repeatfromframe < 0) {
|
|
break;
|
|
}
|
|
|
|
repeattoframe = ptr[-2] << 8 | ptr[-1];
|
|
ptr -= 4;
|
|
|
|
if (repeatfromframe <= apparentframe) {
|
|
if (repeattoframe < apparentframe) {
|
|
result = result - repeattoframe + repeatfromframe - 1;
|
|
} else {
|
|
result = result - apparentframe + repeatfromframe;
|
|
ret = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*frameptr = result;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Return true if the given animation and frame should be skipped.
|
|
*
|
|
* Used by cutscenes.
|
|
*
|
|
* The skip frame numbers are stored at the tail end of the header, prior to the
|
|
* frame repeat data. The frame numbers are stored as a list of shorts.
|
|
* The list is terminated on the left side with a negative value.
|
|
*/
|
|
bool anim_is_frame_cut_skipped(s16 animnum, s32 frame)
|
|
{
|
|
u8 *ptr = (u8 *)(g_AnimHeaderBytes[g_AnimToHeaderSlot[animnum]] + g_Anims[animnum].headerlen - 2);
|
|
|
|
// Iterate past the repeat list
|
|
if (g_Anims[animnum].flags & ANIMFLAG_HASREPEATFRAMES) {
|
|
while (true) {
|
|
s16 repeatfromframe = ptr[0] << 8 | ptr[1];
|
|
|
|
if (repeatfromframe < 0) {
|
|
break;
|
|
}
|
|
|
|
ptr -= 4;
|
|
}
|
|
|
|
ptr -= 2;
|
|
}
|
|
|
|
while (true) {
|
|
s16 skipframe = ptr[0] << 8 | ptr[1];
|
|
|
|
if (skipframe < 0) {
|
|
break;
|
|
}
|
|
|
|
if (skipframe == frame) {
|
|
return true;
|
|
}
|
|
|
|
ptr -= 2;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
u8 anim_load_frame(s16 animnum, s32 framenum)
|
|
{
|
|
s32 slot = -1;
|
|
s32 i;
|
|
s32 offset;
|
|
s32 stack;
|
|
s32 loadframenum = framenum;
|
|
|
|
for (i = 0; i < ANIM_FRAME_CACHE_SIZE; i++) {
|
|
if (g_AnimFrameAnimNums[i] == animnum && g_AnimFrameFrameNums[i] == loadframenum) {
|
|
slot = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (slot >= 0) {
|
|
g_AnimFrameBirths[slot] = 1;
|
|
} else {
|
|
slot = g_NextAnimFrameIndex;
|
|
|
|
while (g_AnimFrameBirths[slot]) {
|
|
slot = (slot + 1) % ANIM_FRAME_CACHE_SIZE;
|
|
}
|
|
|
|
if (g_Anims[animnum].flags & ANIMFLAG_HASREPEATFRAMES) {
|
|
anim_remap_frame_for_load(animnum, framenum, &loadframenum);
|
|
}
|
|
|
|
if (g_Anims[animnum].bytesperframe) {
|
|
offset = g_Anims[animnum].bytesperframe * loadframenum + (g_Anims[animnum].data + g_Anims[animnum].headerlen);
|
|
g_AnimFrameBytes[slot] = anim_dma(&g_AnimFrameByteSlots[slot * g_AnimMaxBytesPerFrame], offset, g_Anims[animnum].bytesperframe);
|
|
} else {
|
|
g_AnimFrameBytes[slot] = &g_AnimFrameByteSlots[slot * g_AnimMaxBytesPerFrame];
|
|
}
|
|
|
|
g_AnimFrameAnimNums[slot] = animnum;
|
|
g_AnimFrameFrameNums[slot] = framenum;
|
|
g_AnimFrameBirths[slot] = 1;
|
|
g_NextAnimFrameIndex = (slot + 1) % ANIM_FRAME_CACHE_SIZE;
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
|
|
void anim_forget_frame_births(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ANIM_FRAME_CACHE_SIZE; i++) {
|
|
g_AnimFrameBirths[i] = 0;
|
|
}
|
|
}
|
|
|
|
void anim_load_header(s16 animnum)
|
|
{
|
|
s32 i;
|
|
|
|
if (g_AnimToHeaderSlot[animnum] != 0xff) {
|
|
g_AnimHeaderBirths[g_AnimToHeaderSlot[animnum]] = g_Vars.thisframestart240;
|
|
g_NextAnimHeaderIndex = (g_AnimToHeaderSlot[animnum] + 1) % ANIM_HEADER_CACHE_SIZE;
|
|
} else {
|
|
s32 tmp;
|
|
s32 slot = g_NextAnimHeaderIndex;
|
|
s32 stack;
|
|
|
|
for (i = 0; i < ANIM_HEADER_CACHE_SIZE; i++) {
|
|
if (g_AnimHeaderBirths[i] < g_AnimHeaderBirths[slot]) {
|
|
slot = i;
|
|
}
|
|
}
|
|
|
|
if (g_AnimHeaderBirths[slot]);
|
|
if (&g_Vars && &g_Vars);
|
|
|
|
if (g_AnimHeaderAnimNums[slot]) {
|
|
g_AnimToHeaderSlot[g_AnimHeaderAnimNums[slot]] = 0xff;
|
|
}
|
|
|
|
tmp = g_Anims[animnum].headerlen;
|
|
|
|
g_AnimHeaderBytes[slot] = anim_dma(&g_AnimHeaderByteSlots[slot * g_AnimMaxHeaderLength], g_Anims[animnum].data, tmp);
|
|
g_AnimToHeaderSlot[animnum] = slot;
|
|
g_AnimHeaderAnimNums[slot] = animnum;
|
|
g_AnimHeaderBirths[slot] = g_Vars.thisframestart240;
|
|
g_NextAnimHeaderIndex = (slot + 1) % ANIM_HEADER_CACHE_SIZE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a number of bits from the given ptr and return it as an integer.
|
|
*
|
|
* remainingbits in the number of bits to read.
|
|
* bitoffset is the starting bit offset relative to ptr.
|
|
*/
|
|
s32 anim_read_bits(u8 *ptr, u8 remainingbits, u32 bitoffset)
|
|
{
|
|
u32 result = 0;
|
|
u32 mask;
|
|
u8 numbitsthisbyte;
|
|
|
|
result *= bitoffset / 8;
|
|
|
|
// Move ptr forward past all the bytes that should be fully skipped
|
|
ptr += bitoffset / 8;
|
|
|
|
// Calculate the number of bits to read in the first byte
|
|
bitoffset %= 8;
|
|
numbitsthisbyte = 8 - bitoffset;
|
|
|
|
// Iterate bytes, except for the last if it's a partial read
|
|
while (remainingbits >= numbitsthisbyte) {
|
|
remainingbits -= numbitsthisbyte;
|
|
mask = (1 << numbitsthisbyte) - 1;
|
|
result |= (*ptr & mask) << remainingbits;
|
|
ptr++;
|
|
numbitsthisbyte = 8;
|
|
}
|
|
|
|
// Read bits from the final byte if it's partial read
|
|
if (remainingbits > 0) {
|
|
mask = (1 << remainingbits) - 1;
|
|
result |= (*ptr >> (numbitsthisbyte - remainingbits)) & mask;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
s32 anim_read_signed_short(u8 *ptr, u8 readbitlen, s32 bitoffset)
|
|
{
|
|
u16 result = anim_read_bits(ptr, readbitlen, bitoffset);
|
|
|
|
if (readbitlen < 16 && (result & (1 << (readbitlen - 1)))) {
|
|
result |= ((1 << (16 - readbitlen)) - 1) << readbitlen;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read the rotation, position and scale values for the given part for the frame
|
|
* at the given frameslot.
|
|
*
|
|
* Both the anim header and frame data must be loaded already.
|
|
*/
|
|
void anim_get_rot_translate_scale(s32 part, bool flip, struct skeleton *skel, s16 animnum, u8 frameslot, struct coord *rot, struct coord *translate, struct coord *scale)
|
|
{
|
|
s32 i;
|
|
u16 introt[3];
|
|
u8 readbitlen;
|
|
u8 *framebytes = g_AnimFrameBytes[frameslot];
|
|
u8 framelen;
|
|
u8 *ptr;
|
|
u8 *end;
|
|
s32 bitoffset;
|
|
u32 stack;
|
|
|
|
if (flip) {
|
|
part = skel->things[part][1];
|
|
}
|
|
|
|
framelen = g_Anims[animnum].framelen;
|
|
ptr = g_AnimHeaderBytes[g_AnimToHeaderSlot[animnum]];
|
|
bitoffset = 0;
|
|
end = ptr + g_Anims[animnum].headerlen;
|
|
|
|
for (i = 0; i < part && ptr < end; i++) {
|
|
u8 flags = *ptr;
|
|
ptr++;
|
|
|
|
if (flags & ANIMFIELD_08) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8] + ptr[11];
|
|
ptr += 12;
|
|
} else if (flags & ANIMFIELD_S16_TRANSLATE) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8];
|
|
ptr += 9;
|
|
} else if (flags & ANIMFIELD_S32_TRANSLATE) {
|
|
bitoffset += ptr[0] + ptr[5] + ptr[10];
|
|
ptr += 15;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_S16_ROTATE) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8];
|
|
ptr += 9;
|
|
} else if (flags & ANIMFIELD_F32_ROTATE) {
|
|
bitoffset += 96;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_CAMERA) {
|
|
bitoffset += ptr[0];
|
|
ptr += 5;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_F32_SCALE) {
|
|
bitoffset += 0x60;
|
|
}
|
|
}
|
|
|
|
if (ptr < end) {
|
|
u8 flags = *ptr;
|
|
ptr++;
|
|
|
|
if (flags & ANIMFIELD_S16_TRANSLATE) {
|
|
readbitlen = ptr[2];
|
|
translate->x = (s16) (anim_read_signed_short(framebytes, readbitlen, bitoffset) + (ptr[0] << 8) + ptr[1]);
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[5];
|
|
translate->y = (s16) (anim_read_signed_short(framebytes, readbitlen, bitoffset) + (ptr[3] << 8) + ptr[4]);
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[8];
|
|
translate->z = (s16) (anim_read_signed_short(framebytes, readbitlen, bitoffset) + (ptr[6] << 8) + ptr[7]);
|
|
bitoffset += readbitlen;
|
|
|
|
ptr += 9;
|
|
} else if (flags & ANIMFIELD_S32_TRANSLATE) {
|
|
readbitlen = ptr[0];
|
|
translate->x = (anim_read_bits(framebytes, readbitlen, bitoffset) + ((ptr[1] << 24) + (ptr[2] << 16) + (ptr[3] << 8) + ptr[4])) * 0.001f;
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[5];
|
|
translate->y = (anim_read_bits(framebytes, readbitlen, bitoffset) + ((ptr[6] << 24) + (ptr[7] << 16) + (ptr[8] << 8) + ptr[9])) * 0.001f;
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[10];
|
|
translate->z = (anim_read_bits(framebytes, readbitlen, bitoffset) + ((ptr[11] << 24) + (ptr[12] << 16) + (ptr[13] << 8) + ptr[14])) * 0.001f;
|
|
bitoffset += readbitlen;
|
|
|
|
ptr += 15;
|
|
} else {
|
|
if (flags & ANIMFIELD_08) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8] + ptr[11];
|
|
ptr += 12;
|
|
}
|
|
|
|
translate->x = translate->y = translate->z = 0.0f;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_S16_ROTATE) {
|
|
readbitlen = ptr[2];
|
|
introt[0] = anim_read_bits(framebytes, readbitlen, bitoffset);
|
|
introt[0] += (ptr[0] << 8) + ptr[1];
|
|
introt[0] <<= 16 - framelen;
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[5];
|
|
introt[1] = anim_read_bits(framebytes, readbitlen, bitoffset);
|
|
introt[1] += (ptr[3] << 8) + ptr[4];
|
|
introt[1] <<= 16 - framelen;
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[8];
|
|
introt[2] = anim_read_bits(framebytes, readbitlen, bitoffset);
|
|
introt[2] += (ptr[6] << 8) + ptr[7];
|
|
introt[2] <<= 16 - framelen;
|
|
bitoffset += readbitlen;
|
|
|
|
rot->x = introt[0] * M_BADTAU / 65536.0f;
|
|
|
|
if (flip) {
|
|
if (introt[1] != 0) {
|
|
rot->y = (0x10000 - introt[1]) * M_BADTAU / 65536.0f;
|
|
} else {
|
|
rot->y = 0.0f;
|
|
}
|
|
|
|
if (introt[2] != 0) {
|
|
rot->z = (0x10000 - introt[2]) * M_BADTAU / 65536.0f;
|
|
} else {
|
|
rot->z = 0.0f;
|
|
}
|
|
} else {
|
|
rot->y = introt[1] * M_BADTAU / 65536.0f;
|
|
rot->z = introt[2] * M_BADTAU / 65536.0f;
|
|
}
|
|
} else if (flags & ANIMFIELD_F32_ROTATE) {
|
|
s32 sp38;
|
|
|
|
sp38 = anim_read_bits(framebytes, 32, bitoffset);
|
|
rot->x = *(f32 *)&sp38;
|
|
bitoffset += 32;
|
|
|
|
sp38 = anim_read_bits(framebytes, 32, bitoffset);
|
|
rot->y = *(f32 *)&sp38;
|
|
bitoffset += 32;
|
|
|
|
sp38 = anim_read_bits(framebytes, 32, bitoffset);
|
|
rot->z = *(f32 *)&sp38;
|
|
bitoffset += 32;
|
|
|
|
if (flip) {
|
|
if (rot->y != 0.0f) {
|
|
rot->y = M_BADTAU - rot->y;
|
|
}
|
|
|
|
if (rot->z != 0.0f) {
|
|
rot->z = M_BADTAU - rot->z;
|
|
}
|
|
}
|
|
} else {
|
|
rot->x = rot->y = rot->z = 0.0f;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_F32_SCALE) {
|
|
s32 word;
|
|
|
|
word = anim_read_bits(framebytes, 32, bitoffset);
|
|
scale->x = *(f32 *)&word;
|
|
bitoffset += 32;
|
|
|
|
word = anim_read_bits(framebytes, 32, bitoffset);
|
|
scale->y = *(f32 *)&word;
|
|
bitoffset += 32;
|
|
|
|
word = anim_read_bits(framebytes, 32, bitoffset);
|
|
scale->z = *(f32 *)&word;
|
|
} else {
|
|
scale->x = scale->y = scale->z = 1.0f;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
rot->x = rot->y = rot->z = 0.0f;
|
|
translate->x = translate->y = translate->z = 0.0f;
|
|
scale->x = scale->y = scale->z = 1.0f;
|
|
}
|
|
|
|
/**
|
|
* Read the position and Y rotation (?) values for the given part at the given
|
|
* frame number.
|
|
*
|
|
* No data needs to be loaded by the caller - the function will ensure the
|
|
* header and frame are loaded.
|
|
*/
|
|
u16 anim_get_pos_angle_as_int(s32 part, bool flip, struct skeleton *skel, s16 animnum, s32 framenum, s16 inttranslate[3], bool use_cache)
|
|
{
|
|
u16 result = 0;
|
|
s32 bitoffset;
|
|
u8 readbitlen;
|
|
u8 slot;
|
|
u8 *framebytes;
|
|
u8 *ptr;
|
|
s32 i;
|
|
|
|
if (use_cache) {
|
|
inttranslate[0] = 0;
|
|
inttranslate[1] = 0;
|
|
inttranslate[2] = g_AnimAverageMoveDist[animnum];
|
|
} else {
|
|
anim_load_header(animnum);
|
|
slot = anim_load_frame(animnum, framenum);
|
|
anim_forget_frame_births();
|
|
|
|
framebytes = g_AnimFrameBytes[slot];
|
|
|
|
if (flip) {
|
|
part = skel->things[part][1];
|
|
}
|
|
|
|
bitoffset = 0;
|
|
ptr = g_AnimHeaderBytes[g_AnimToHeaderSlot[animnum]];
|
|
|
|
for (i = 0; i < part; i++) {
|
|
u8 flags = *ptr;
|
|
ptr++;
|
|
|
|
if (flags & ANIMFIELD_08) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8] + ptr[11];
|
|
ptr += 12;
|
|
} else if (flags & ANIMFIELD_S16_TRANSLATE) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8];
|
|
ptr += 9;
|
|
} else if (flags & ANIMFIELD_S32_TRANSLATE) {
|
|
bitoffset += ptr[0] + ptr[5] + ptr[10];
|
|
ptr += 15;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_S16_ROTATE) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8];
|
|
ptr += 9;
|
|
} else if (flags & ANIMFIELD_F32_ROTATE) {
|
|
bitoffset += 96;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_CAMERA) {
|
|
bitoffset += *ptr;
|
|
ptr += 5;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_F32_SCALE) {
|
|
bitoffset += 96;
|
|
}
|
|
}
|
|
|
|
readbitlen = ptr[3];
|
|
inttranslate[0] = anim_read_signed_short(framebytes, readbitlen, bitoffset) + ptr[1] * 256 + ptr[2];
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[6];
|
|
inttranslate[1] = anim_read_signed_short(framebytes, readbitlen, bitoffset) + ptr[4] * 256 + ptr[5];
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[9];
|
|
inttranslate[2] = anim_read_signed_short(framebytes, readbitlen, bitoffset) + ptr[7] * 256 + ptr[8];
|
|
bitoffset += readbitlen;
|
|
|
|
readbitlen = ptr[12];
|
|
result = anim_read_signed_short(framebytes, readbitlen, bitoffset) + ptr[10] * 256 + ptr[11];
|
|
|
|
if (flip) {
|
|
inttranslate[0] = -inttranslate[0];
|
|
|
|
if (result != 0) {
|
|
result = 0x10000 - result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
f32 anim_get_translate_angle(s32 part, bool flip, struct skeleton *skel, s16 animnum, s32 framenum, struct coord *translate, bool arg6)
|
|
{
|
|
s16 inttranslate[3];
|
|
|
|
f32 angle = anim_get_pos_angle_as_int(part, flip, skel, animnum, framenum, inttranslate, arg6);
|
|
|
|
translate->x = inttranslate[0];
|
|
translate->y = inttranslate[1];
|
|
translate->z = inttranslate[2];
|
|
|
|
return angle * M_BADTAU / 65536.0f;
|
|
}
|
|
|
|
/**
|
|
* Return a camera value (FOV Y or blur frac) for the current frame.
|
|
*
|
|
* The function assumes the current frame's data has been loaded.
|
|
* Its slot is provided by the frameslot argument.
|
|
*
|
|
* When part = 1, the returned value is the FOV Y.
|
|
* When part = 2, the returned value is the blur frac.
|
|
*/
|
|
f32 anim_get_camera_value(s32 part, s16 animnum, u8 frameslot)
|
|
{
|
|
u32 stack[2];
|
|
u8 *framebytes = g_AnimFrameBytes[frameslot];
|
|
u8 *ptr = g_AnimHeaderBytes[g_AnimToHeaderSlot[animnum]];
|
|
f32 result = 0;
|
|
s32 bitoffset = 0;
|
|
s32 i;
|
|
u8 *end = ptr + g_Anims[animnum].headerlen;
|
|
|
|
for (i = 0; i < part && ptr < end; i++) {
|
|
u8 flags = ptr[0];
|
|
ptr++;
|
|
|
|
if (flags & ANIMFIELD_08) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8] + ptr[11];
|
|
ptr += 12;
|
|
} else if (flags & ANIMFIELD_S16_TRANSLATE) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8];
|
|
ptr += 9;
|
|
} else if (flags & ANIMFIELD_S32_TRANSLATE) {
|
|
bitoffset += ptr[0] + ptr[5] + ptr[10];
|
|
ptr += 15;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_S16_ROTATE) {
|
|
bitoffset += ptr[2] + ptr[5] + ptr[8];
|
|
ptr += 9;
|
|
} else if (flags & ANIMFIELD_F32_ROTATE) {
|
|
bitoffset += 0x60;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_CAMERA) {
|
|
bitoffset += ptr[0];
|
|
ptr += 5;
|
|
}
|
|
|
|
if (flags & ANIMFIELD_F32_SCALE) {
|
|
bitoffset += 0x60;
|
|
}
|
|
}
|
|
|
|
if (ptr < end) {
|
|
u8 flags = ptr[0];
|
|
ptr++;
|
|
|
|
if (flags & ANIMFIELD_CAMERA) {
|
|
/**
|
|
* In the header:
|
|
* ptr[0] = number of bits to read in the frame data
|
|
* ptr[1,2,3,4] = base value
|
|
*
|
|
* The value in the frame data is an adjustment value that is added
|
|
* to the base value.
|
|
*/
|
|
s32 framevalue = anim_read_bits(framebytes, ptr[0], bitoffset);
|
|
result = (framevalue + ptr[1] * 0x1000000 + ptr[2] * 0x10000 + ptr[3] * 0x100 + ptr[4]) * 0.001f;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|