librw/src/hanim.cpp
2017-08-09 11:01:43 +02:00

341 lines
8.2 KiB
C++

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include "rwbase.h"
#include "rwerror.h"
#include "rwplg.h"
#include "rwpipeline.h"
#include "rwobjects.h"
#include "rwengine.h"
#include "rwanim.h"
#include "rwplugins.h"
#include "ps2/rwps2.h"
#include "ps2/rwps2plg.h"
#include "d3d/rwxbox.h"
#include "d3d/rwd3d8.h"
#include "d3d/rwd3d9.h"
#include "gl/rwwdgl.h"
#include "gl/rwgl3.h"
#define PLUGIN_ID ID_HANIM
namespace rw {
int32 hAnimOffset;
bool32 hAnimDoStream = 1;
HAnimHierarchy*
HAnimHierarchy::create(int32 numNodes, int32 *nodeFlags, int32 *nodeIDs,
int32 flags, int32 maxKeySize)
{
HAnimHierarchy *hier = (HAnimHierarchy*)malloc(sizeof(*hier));
hier->currentAnim = AnimInterpolator::create(numNodes, maxKeySize);
hier->numNodes = numNodes;
hier->flags = flags;
hier->parentFrame = nil;
hier->parentHierarchy = hier;
if(hier->flags & NOMATRICES){
hier->matrices = nil;
hier->matricesUnaligned = nil;
}else{
hier->matricesUnaligned =
(void*) new uint8[hier->numNodes*64 + 15];
hier->matrices =
(Matrix*)((uintptr)hier->matricesUnaligned & ~0xF);
}
hier->nodeInfo = new HAnimNodeInfo[hier->numNodes];
for(int32 i = 0; i < hier->numNodes; i++){
hier->nodeInfo[i].id = nodeIDs[i];
hier->nodeInfo[i].index = i;
hier->nodeInfo[i].flags = nodeFlags[i];
hier->nodeInfo[i].frame = nil;
}
return hier;
}
void
HAnimHierarchy::destroy(void)
{
delete[] (uint8*)this->matricesUnaligned;
delete[] this->nodeInfo;
free(this);
}
static Frame*
findById(Frame *f, int32 id)
{
if(f == nil) return nil;
HAnimData *hanim = HAnimData::get(f);
if(hanim->id >= 0 && hanim->id == id) return f;
Frame *ff = findById(f->next, id);
if(ff) return ff;
return findById(f->child, id);
}
void
HAnimHierarchy::attachByIndex(int32 idx)
{
int32 id = this->nodeInfo[idx].id;
Frame *f = findById(this->parentFrame, id);
this->nodeInfo[idx].frame = f;
}
void
HAnimHierarchy::attach(void)
{
for(int32 i = 0; i < this->numNodes; i++)
this->attachByIndex(i);
}
int32
HAnimHierarchy::getIndex(int32 id)
{
for(int32 i = 0; i < this->numNodes; i++)
if(this->nodeInfo[i].id == id)
return i;
return -1;
}
HAnimHierarchy*
HAnimHierarchy::get(Frame *f)
{
return HAnimData::get(f)->hierarchy;
}
HAnimHierarchy*
HAnimHierarchy::find(Frame *f)
{
if(f == nil) return nil;
HAnimHierarchy *hier = HAnimHierarchy::get(f);
if(hier) return hier;
hier = HAnimHierarchy::find(f->next);
if(hier) return hier;
return HAnimHierarchy::find(f->child);
}
void
HAnimHierarchy::updateMatrices(void)
{
// TODO: handle more (all!) cases
Matrix rootMat, animMat;
Matrix *curMat, *parentMat;
Matrix **sp, *stack[64];
Frame *frm, *parfrm;
int32 i;
AnimInterpolator *anim = this->currentAnim;
sp = stack;
curMat = this->matrices;
frm = this->parentFrame;
if(frm && (parfrm = frm->getParent()))
rootMat = *parfrm->getLTM();
else
rootMat.setIdentity();
parentMat = &rootMat;
HAnimNodeInfo *node = this->nodeInfo;
for(i = 0; i < this->numNodes; i++){
anim->applyCB(&animMat, anim->getInterpFrame(i));
Matrix::mult(curMat, parentMat, &animMat);
if(node->flags & PUSH)
*sp++ = parentMat;
parentMat = curMat;
if(node->flags & POP)
parentMat = *--sp;
node++;
curMat++;
}
}
HAnimData*
HAnimData::get(Frame *f)
{
return PLUGINOFFSET(HAnimData, f, hAnimOffset);
}
static void*
createHAnim(void *object, int32 offset, int32)
{
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
hanim->id = -1;
hanim->hierarchy = nil;
return object;
}
static void*
destroyHAnim(void *object, int32 offset, int32)
{
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
if(hanim->hierarchy)
hanim->hierarchy->destroy();
hanim->id = -1;
hanim->hierarchy = nil;
return object;
}
static void*
copyHAnim(void *dst, void *src, int32 offset, int32)
{
HAnimData *dsthanim = PLUGINOFFSET(HAnimData, dst, offset);
HAnimData *srchanim = PLUGINOFFSET(HAnimData, src, offset);
dsthanim->id = srchanim->id;
// TODO
dsthanim->hierarchy = nil;
return dst;
}
static Stream*
readHAnim(Stream *stream, int32, void *object, int32 offset, int32)
{
int32 ver, numNodes;
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
ver = stream->readI32();
assert(ver == 0x100);
hanim->id = stream->readI32();
numNodes = stream->readI32();
if(numNodes != 0){
int32 flags = stream->readI32();
int32 maxKeySize = stream->readI32();
// Sizes are fucked for 64 bit pointers but
// AnimInterpolator::create() will take care of that
int32 *nodeFlags = new int32[numNodes];
int32 *nodeIDs = new int32[numNodes];
for(int32 i = 0; i < numNodes; i++){
nodeIDs[i] = stream->readI32();
stream->readI32(); // index...unused
nodeFlags[i] = stream->readI32();
}
hanim->hierarchy = HAnimHierarchy::create(numNodes,
nodeFlags, nodeIDs, flags, maxKeySize);
hanim->hierarchy->parentFrame = (Frame*)object;
delete[] nodeFlags;
delete[] nodeIDs;
}
return stream;
}
static Stream*
writeHAnim(Stream *stream, int32, void *object, int32 offset, int32)
{
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
stream->writeI32(256);
stream->writeI32(hanim->id);
if(hanim->hierarchy == nil){
stream->writeI32(0);
return stream;
}
HAnimHierarchy *hier = hanim->hierarchy;
stream->writeI32(hier->numNodes);
stream->writeI32(hier->flags);
stream->writeI32(hier->currentAnim->maxInterpKeyFrameSize);
for(int32 i = 0; i < hier->numNodes; i++){
stream->writeI32(hier->nodeInfo[i].id);
stream->writeI32(hier->nodeInfo[i].index);
stream->writeI32(hier->nodeInfo[i].flags);
}
return stream;
}
static int32
getSizeHAnim(void *object, int32 offset, int32)
{
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
if(!hAnimDoStream ||
version >= 0x35000 && hanim->id == -1 && hanim->hierarchy == nil)
return 0;
if(hanim->hierarchy)
return 12 + 8 + hanim->hierarchy->numNodes*12;
return 12;
}
static void
hAnimFrameRead(Stream *stream, Animation *anim)
{
HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes;
for(int32 i = 0; i < anim->numFrames; i++){
frames[i].time = stream->readF32();
stream->read(&frames[i].q, 4*4);
stream->read(&frames[i].t, 3*4);
int32 prev = stream->readI32()/0x24;
frames[i].prev = &frames[prev];
}
}
static void
hAnimFrameWrite(Stream *stream, Animation *anim)
{
HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes;
for(int32 i = 0; i < anim->numFrames; i++){
stream->writeF32(frames[i].time);
stream->write(&frames[i].q, 4*4);
stream->write(&frames[i].t, 3*4);
stream->writeI32((frames[i].prev - frames)*0x24);
}
}
static uint32
hAnimFrameGetSize(Animation *anim)
{
return anim->numFrames*(4 + 4*4 + 3*4 + 4);
}
//void hanimBlendCB(void *out, void *in1, void *in2, float32 a);
//void hanimAddCB(void *out, void *in1, void *in2);
//void hanimMulRecipCB(void *frame, void *start);
static void
hanimApplyCB(void *result, void *frame)
{
Matrix *m = (Matrix*)result;
HAnimInterpFrame *f = (HAnimInterpFrame*)frame;
m->rotate(f->q, COMBINEREPLACE);
m->pos = f->t;
}
static void
hanimInterpCB(void *vout, void *vin1, void *vin2, float32 t, void*)
{
HAnimInterpFrame *out = (HAnimInterpFrame*)vout;
HAnimKeyFrame *in1 = (HAnimKeyFrame*)vin1;
HAnimKeyFrame *in2 = (HAnimKeyFrame*)vin2;
assert(t >= in1->time && t <= in2->time);
float32 a = (t - in1->time)/(in2->time - in1->time);
out->t = lerp(in1->t, in2->t, a);
out->q = slerp(in1->q, in2->q, a);
}
void
registerHAnimPlugin(void)
{
hAnimOffset = Frame::registerPlugin(sizeof(HAnimData), ID_HANIMPLUGIN,
createHAnim,
destroyHAnim, copyHAnim);
Frame::registerPluginStream(ID_HANIMPLUGIN,
readHAnim,
writeHAnim,
getSizeHAnim);
AnimInterpolatorInfo *info = new AnimInterpolatorInfo;
info->id = 1;
info->interpKeyFrameSize = sizeof(HAnimInterpFrame);
info->animKeyFrameSize = sizeof(HAnimKeyFrame);
info->customDataSize = 0;
info->applyCB = hanimApplyCB;
info->blendCB = nil;
info->interpCB = hanimInterpCB;
info->addCB = nil;
info->mulRecipCB = nil;
info->streamRead = hAnimFrameRead;
info->streamWrite = hAnimFrameWrite;
info->streamGetSize = hAnimFrameGetSize;
AnimInterpolatorInfo::registerInterp(info);
}
}