summaryrefslogtreecommitdiff
path: root/src/anim.cpp
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
committerclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
commit847962910f0bff071f3bf07c9abb87764fb6cac3 (patch)
treeddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /src/anim.cpp
downloadlibrw-master.tar.gz
librw-master.zip
Import aap/librw @ masterHEADmaster
Snapshot for re3/reVC vendoring, per @lzcnt. Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src/anim.cpp')
-rw-r--r--src/anim.cpp293
1 files changed, 293 insertions, 0 deletions
diff --git a/src/anim.cpp b/src/anim.cpp
new file mode 100644
index 0000000..2003f1b
--- /dev/null
+++ b/src/anim.cpp
@@ -0,0 +1,293 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwanim.h"
+#include "rwplugins.h"
+
+#define PLUGIN_ID ID_ANIMANIMATION
+
+namespace rw {
+
+//
+// AnimInterpolatorInfo
+//
+
+#define MAXINTERPINFO 10
+
+static AnimInterpolatorInfo *interpInfoList[MAXINTERPINFO];
+
+void
+AnimInterpolatorInfo::registerInterp(AnimInterpolatorInfo *interpInfo)
+{
+ for(int32 i = 0; i < MAXINTERPINFO; i++)
+ if(interpInfoList[i] == nil){
+ interpInfoList[i] = interpInfo;
+ return;
+ }
+ assert(0 && "no room for interpolatorInfo");
+}
+
+void
+AnimInterpolatorInfo::unregisterInterp(AnimInterpolatorInfo *interpInfo)
+{
+ for(int32 i = 0; i < MAXINTERPINFO; i++)
+ if(interpInfoList[i] == interpInfo){
+ rwFree(interpInfoList[i]);
+ interpInfoList[i] = nil;
+ return;
+ }
+}
+
+AnimInterpolatorInfo*
+AnimInterpolatorInfo::find(int32 id)
+{
+ for(int32 i = 0; i < MAXINTERPINFO; i++){
+ if(interpInfoList[i] && interpInfoList[i]->id == id)
+ return interpInfoList[i];
+ }
+ return nil;
+}
+
+//
+// Animation
+//
+
+Animation*
+Animation::create(AnimInterpolatorInfo *interpInfo, int32 numFrames,
+ int32 flags, float duration)
+{
+ int32 sz = sizeof(Animation) +
+ numFrames*interpInfo->animKeyFrameSize +
+ interpInfo->customDataSize;
+ uint8 *data = (uint8*)rwMalloc(sz, MEMDUR_EVENT | ID_ANIMANIMATION);
+ if(data == nil){
+ RWERROR((ERR_ALLOC, sz));
+ return nil;
+ }
+ Animation *anim = (Animation*)data;
+ data += sizeof(Animation);
+ anim->interpInfo = interpInfo;
+ anim->numFrames = numFrames;
+ anim->flags = flags;
+ anim->duration = duration;
+ anim->keyframes = data;
+ data += anim->numFrames*interpInfo->animKeyFrameSize;
+ anim->customData = data;
+ return anim;
+}
+
+void
+Animation::destroy(void)
+{
+ rwFree(this);
+}
+
+int32
+Animation::getNumNodes(void)
+{
+ int32 sz = this->interpInfo->animKeyFrameSize;
+ KeyFrameHeader *first = (KeyFrameHeader*)this->keyframes;
+ int32 n = 0;
+ for(KeyFrameHeader *f = first; f->prev != first; f = f->next(sz))
+ n++;
+ return n;
+}
+
+Animation*
+Animation::streamRead(Stream *stream)
+{
+ Animation *anim;
+ if(stream->readI32() != 0x100)
+ return nil;
+ int32 typeID = stream->readI32();
+ AnimInterpolatorInfo *interpInfo = AnimInterpolatorInfo::find(typeID);
+ int32 numFrames = stream->readI32();
+ int32 flags = stream->readI32();
+ float duration = stream->readF32();
+ anim = Animation::create(interpInfo, numFrames, flags, duration);
+ interpInfo->streamRead(stream, anim);
+ return anim;
+}
+
+Animation*
+Animation::streamReadLegacy(Stream *stream)
+{
+ Animation *anim;
+ AnimInterpolatorInfo *interpInfo = AnimInterpolatorInfo::find(1);
+ int32 numFrames = stream->readI32();
+ int32 flags = stream->readI32();
+ float duration = stream->readF32();
+ anim = Animation::create(interpInfo, numFrames, flags, duration);
+ HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes;
+ for(int32 i = 0; i < anim->numFrames; i++){
+ stream->read32(&frames[i].q, 4*4);
+ frames[i].q = conj(frames[i].q);
+ stream->read32(&frames[i].t, 3*4);
+ frames[i].time = stream->readF32();
+ int32 prev = stream->readI32()/0x24;
+ frames[i].prev = &frames[prev];
+ }
+ return anim;
+}
+
+bool
+Animation::streamWrite(Stream *stream)
+{
+ writeChunkHeader(stream, ID_ANIMANIMATION, this->streamGetSize());
+ stream->writeI32(0x100);
+ stream->writeI32(this->interpInfo->id);
+ stream->writeI32(this->numFrames);
+ stream->writeI32(this->flags);
+ stream->writeF32(this->duration);
+ this->interpInfo->streamWrite(stream, this);
+ return true;
+}
+
+bool
+Animation::streamWriteLegacy(Stream *stream)
+{
+ stream->writeI32(this->numFrames);
+ stream->writeI32(this->flags);
+ stream->writeF32(this->duration);
+ assert(interpInfo->id == 1);
+ HAnimKeyFrame *frames = (HAnimKeyFrame*)this->keyframes;
+ for(int32 i = 0; i < this->numFrames; i++){
+ frames[i].q = conj(frames[i].q);
+ stream->write32(&frames[i].q, 4*4);
+ frames[i].q = conj(frames[i].q);
+ stream->write32(&frames[i].t, 3*4);
+ stream->writeF32(frames[i].time);
+ stream->writeI32((frames[i].prev - frames)*0x24);
+ }
+ return true;
+}
+
+uint32
+Animation::streamGetSize(void)
+{
+ uint32 size = 4 + 4 + 4 + 4 + 4;
+ size += this->interpInfo->streamGetSize(this);
+ return size;
+}
+
+//
+// AnimInterpolator
+//
+
+AnimInterpolator*
+AnimInterpolator::create(int32 numNodes, int32 maxFrameSize)
+{
+ AnimInterpolator *interp;
+ int32 sz;
+ int32 realsz = maxFrameSize;
+
+ // Add some space for pointers and padding, hopefully this will be
+ // enough. Don't change maxFrameSize not to mess up streaming.
+ if(sizeof(void*) > 4)
+ realsz += 16;
+ sz = sizeof(AnimInterpolator) + numNodes*realsz;
+ interp = (AnimInterpolator*)rwMalloc(sz, MEMDUR_EVENT | ID_ANIMANIMATION);
+ if(interp == nil){
+ RWERROR((ERR_ALLOC, sz));
+ return nil;
+ }
+ interp->currentAnim = nil;
+ interp->currentTime = 0.0f;
+ interp->nextFrame = nil;
+ interp->maxInterpKeyFrameSize = maxFrameSize;
+ interp->currentInterpKeyFrameSize = maxFrameSize;
+ interp->currentAnimKeyFrameSize = -1;
+ interp->numNodes = numNodes;;
+
+ return interp;
+}
+
+void
+AnimInterpolator::destroy(void)
+{
+ rwFree(this);
+}
+
+bool32
+AnimInterpolator::setCurrentAnim(Animation *anim)
+{
+ int32 i;
+ AnimInterpolatorInfo *interpInfo = anim->interpInfo;
+ this->currentAnim = anim;
+ this->currentTime = 0.0f;
+ int32 maxkf = this->maxInterpKeyFrameSize;
+ if(sizeof(void*) > 4) // see above in create()
+ maxkf += 16;
+ if(interpInfo->interpKeyFrameSize > maxkf){
+ RWERROR((ERR_GENERAL, "interpolation frame too big"));
+ return 0;
+ }
+ this->currentInterpKeyFrameSize = interpInfo->interpKeyFrameSize;
+ this->currentAnimKeyFrameSize = interpInfo->animKeyFrameSize;
+ this->applyCB = interpInfo->applyCB;
+ this->blendCB = interpInfo->blendCB;
+ this->interpCB = interpInfo->interpCB;
+ this->addCB = interpInfo->addCB;
+ for(i = 0; i < numNodes; i++){
+ InterpFrameHeader *intf;
+ KeyFrameHeader *kf1, *kf2;
+ intf = this->getInterpFrame(i);
+ kf1 = this->getAnimFrame(i);
+ kf2 = this->getAnimFrame(i+numNodes);
+ intf->keyFrame1 = kf1;
+ intf->keyFrame2 = kf2;
+ // TODO: perhaps just implement all interpolator infos?
+ if(this->interpCB)
+ this->interpCB(intf, kf1, kf2, 0.0f, anim->customData);
+ }
+ this->nextFrame = this->getAnimFrame(numNodes*2);
+ return 1;
+}
+
+void
+AnimInterpolator::addTime(float32 t)
+{
+ int32 i;
+ if(t <= 0.0f)
+ return;
+ this->currentTime += t;
+ // reset animation
+ if(this->currentTime > this->currentAnim->duration){
+ this->setCurrentAnim(this->currentAnim);
+ return;
+ }
+ KeyFrameHeader *last = this->getAnimFrame(this->currentAnim->numFrames);
+ KeyFrameHeader *next = (KeyFrameHeader*)this->nextFrame;
+ InterpFrameHeader *ifrm = nil;
+ while(next < last && next->prev->time <= this->currentTime){
+ // find next interpolation frame to expire
+ for(i = 0; i < this->numNodes; i++){
+ ifrm = this->getInterpFrame(i);
+ if(ifrm->keyFrame2 == next->prev)
+ break;
+ }
+ // advance interpolation frame
+ ifrm->keyFrame1 = ifrm->keyFrame2;
+ ifrm->keyFrame2 = next;
+ // ... and next frame
+ next = (KeyFrameHeader*)((uint8*)this->nextFrame +
+ currentAnimKeyFrameSize);
+ this->nextFrame = next;
+ }
+ for(i = 0; i < this->numNodes; i++){
+ ifrm = this->getInterpFrame(i);
+ this->interpCB(ifrm, ifrm->keyFrame1, ifrm->keyFrame2,
+ this->currentTime,
+ this->currentAnim->customData);
+ }
+}
+
+}