diff options
| author | claude-bot <[email protected]> | 2026-07-13 12:40:03 +0000 |
|---|---|---|
| committer | claude-bot <[email protected]> | 2026-07-13 12:40:03 +0000 |
| commit | 847962910f0bff071f3bf07c9abb87764fb6cac3 (patch) | |
| tree | ddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /src/hanim.cpp | |
| download | librw-847962910f0bff071f3bf07c9abb87764fb6cac3.tar.gz librw-847962910f0bff071f3bf07c9abb87764fb6cac3.zip | |
Snapshot for re3/reVC vendoring, per @lzcnt.
Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src/hanim.cpp')
| -rw-r--r-- | src/hanim.cpp | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/src/hanim.cpp b/src/hanim.cpp new file mode 100644 index 0000000..6bc7c5e --- /dev/null +++ b/src/hanim.cpp @@ -0,0 +1,414 @@ +#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" +#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*)rwMalloc(sizeof(*hier), MEMDUR_EVENT | ID_HANIM); + if(hier == nil){ + RWERROR((ERR_ALLOC, sizeof(*hier))); + return nil; + } + hier->interpolator = 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 = rwNew(hier->numNodes*64 + 0xF, MEMDUR_EVENT | ID_HANIM); + hier->matrices = + (Matrix*)(((uintptr)hier->matricesUnaligned + 0xF) & ~0xF); + } + hier->nodeInfo = rwNewT(HAnimNodeInfo, hier->numNodes, MEMDUR_EVENT | ID_HANIM); + for(int32 i = 0; i < hier->numNodes; i++){ + if(nodeIDs) + hier->nodeInfo[i].id = nodeIDs[i]; + else + hier->nodeInfo[i].id = 0; + hier->nodeInfo[i].index = i; + if(nodeFlags) + hier->nodeInfo[i].flags = nodeFlags[i]; + else + hier->nodeInfo[i].flags = 0; + hier->nodeInfo[i].frame = nil; + } + return hier; +} + +void +HAnimHierarchy::destroy(void) +{ + this->interpolator->destroy(); + rwFree(this->matricesUnaligned); + rwFree(this->nodeInfo); + rwFree(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); +} + +static Frame* +findUnattachedById(HAnimHierarchy *hier, Frame *f, int32 id) +{ + if(f == nil) return nil; + HAnimData *hanim = HAnimData::get(f); + if(hanim->id >= 0 && hanim->id == id && hier->getIndex(f) == -1) return f; + Frame *ff = findUnattachedById(hier, f->next, id); + if(ff) return ff; + return findUnattachedById(hier, f->child, id); +} + +void +HAnimHierarchy::attachByIndex(int32 idx) +{ + int32 id = this->nodeInfo[idx].id; +// Frame *f = findById(this->parentFrame, id); + Frame *f = findUnattachedById(this, this->parentFrame, id); + if(f) + 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; +} + +int32 +HAnimHierarchy::getIndex(Frame *f) +{ + for(int32 i = 0; i < this->numNodes; i++) + if(this->nodeInfo[i].frame == f) + 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->interpolator; + + sp = stack; + curMat = this->matrices; + + frm = this->parentFrame; + if(frm && (parfrm = frm->getParent()) && !(this->flags&LOCALSPACEMATRICES)) + rootMat = *parfrm->getLTM(); + else + rootMat.setIdentity(); + parentMat = &rootMat; + *sp++ = parentMat; + HAnimNodeInfo *node = this->nodeInfo; + for(i = 0; i < this->numNodes; i++){ + anim->applyCB(&animMat, anim->getInterpFrame(i)); + + // TODO: here we could update local matrices + + Matrix::mult(curMat, &animMat, parentMat); + + // TODO: here we could update LTM + + if(node->flags & PUSH) + *sp++ = parentMat; + parentMat = curMat; + if(node->flags & POP) + parentMat = *--sp; + assert(sp >= stack); + assert(sp <= &stack[64]); + + 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) +{ + int i; + HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset); + if(hanim->hierarchy){ + for(i = 0; i < hanim->hierarchy->numNodes; i++) + hanim->hierarchy->nodeInfo[i].frame = nil; + if(object == hanim->hierarchy->parentFrame) + hanim->hierarchy->destroy(); + } + hanim->id = -1; + hanim->hierarchy = nil; + return object; +} + +static void* +copyHAnim(void *dst, void *src, int32 offset, int32) +{ + int i; + HAnimData *dsthanim = PLUGINOFFSET(HAnimData, dst, offset); + HAnimData *srchanim = PLUGINOFFSET(HAnimData, src, offset); + HAnimHierarchy *srchier, *dsthier; + dsthanim->id = srchanim->id; + dsthanim->hierarchy = nil; + srchier = srchanim->hierarchy; + if(srchier && !(srchier->flags & HAnimHierarchy::SUBHIERARCHY)){ + dsthier = HAnimHierarchy::create(srchier->numNodes, nil, nil, srchier->flags, srchier->interpolator->maxInterpKeyFrameSize); + for(i = 0; i < dsthier->numNodes; i++){ + dsthier->nodeInfo[i].frame = nil; + dsthier->nodeInfo[i].flags = srchier->nodeInfo[i].flags; + dsthier->nodeInfo[i].index = srchier->nodeInfo[i].index; + dsthier->nodeInfo[i].id = srchier->nodeInfo[i].id; + } + dsthanim->hierarchy = dsthier; + dsthier->parentFrame = (Frame*)dst; + } + 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 = rwNewT(int32, numNodes, + MEMDUR_FUNCTION | ID_HANIM); + int32 *nodeIDs = rwNewT(int32, numNodes, + MEMDUR_FUNCTION | ID_HANIM); + 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; + rwFree(nodeFlags); + rwFree(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->interpolator->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->read32(&frames[i].q, 4*4); + stream->read32(&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->write32(&frames[i].q, 4*4); + stream->write32(&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); +} + +static void* +hanimOpen(void *object, int32 offset, int32 size) +{ + AnimInterpolatorInfo *info = rwNewT(AnimInterpolatorInfo, 1, MEMDUR_GLOBAL | ID_HANIM); + 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); + return object; +} + +static void* +hanimClose(void *object, int32 offset, int32 size) +{ + AnimInterpolatorInfo::unregisterInterp(AnimInterpolatorInfo::find(1)); + return object; +} + + +void +registerHAnimPlugin(void) +{ + Engine::registerPlugin(0, ID_HANIM, hanimOpen, hanimClose); + hAnimOffset = Frame::registerPlugin(sizeof(HAnimData), ID_HANIM, + createHAnim, + destroyHAnim, copyHAnim); + Frame::registerPluginStream(ID_HANIM, + readHAnim, + writeHAnim, + getSizeHAnim); +} + +} |
