diff --git a/rw.h b/rw.h index 2773223..8638793 100644 --- a/rw.h +++ b/rw.h @@ -6,6 +6,7 @@ #include "src/rwplg.h" #include "src/rwpipeline.h" #include "src/rwobjects.h" +#include "src/rwanim.h" #include "src/rwengine.h" #include "src/rwplugins.h" #include "src/ps2/rwps2.h" diff --git a/src/anim.cpp b/src/anim.cpp index 57c5ae6..8194b71 100644 --- a/src/anim.cpp +++ b/src/anim.cpp @@ -8,18 +8,23 @@ #include "rwplg.h" #include "rwpipeline.h" #include "rwobjects.h" +#include "rwanim.h" #include "rwplugins.h" -#define PLUGIN_ID 2 // ? +#define PLUGIN_ID 0 namespace rw { +// +// AnimInterpolatorInfo +// + #define MAXINTERPINFO 10 static AnimInterpolatorInfo *interpInfoList[MAXINTERPINFO]; void -registerAnimInterpolatorInfo(AnimInterpolatorInfo *interpInfo) +AnimInterpolatorInfo::registerInterp(AnimInterpolatorInfo *interpInfo) { for(int32 i = 0; i < MAXINTERPINFO; i++) if(interpInfoList[i] == nil){ @@ -30,7 +35,7 @@ registerAnimInterpolatorInfo(AnimInterpolatorInfo *interpInfo) } AnimInterpolatorInfo* -findAnimInterpolatorInfo(int32 id) +AnimInterpolatorInfo::find(int32 id) { for(int32 i = 0; i < MAXINTERPINFO; i++){ if(interpInfoList[i] && interpInfoList[i]->id == id) @@ -39,17 +44,30 @@ findAnimInterpolatorInfo(int32 id) return nil; } +// +// Animation +// + Animation* -Animation::create(AnimInterpolatorInfo *interpInfo, int32 numFrames, int32 flags, float duration) +Animation::create(AnimInterpolatorInfo *interpInfo, int32 numFrames, + int32 flags, float duration) { - Animation *anim = (Animation*)malloc(sizeof(*anim)); + int32 sz = sizeof(Animation) + + numFrames*interpInfo->animKeyFrameSize + + interpInfo->customDataSize; + uint8 *data = (uint8*)malloc(sz); + 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; - uint8 *data = new uint8[anim->numFrames*interpInfo->keyFrameSize + interpInfo->customDataSize]; anim->keyframes = data; - data += anim->numFrames*interpInfo->keyFrameSize; + data += anim->numFrames*interpInfo->animKeyFrameSize; anim->customData = data; return anim; } @@ -57,11 +75,20 @@ Animation::create(AnimInterpolatorInfo *interpInfo, int32 numFrames, int32 flags void Animation::destroy(void) { - uint8 *c = (uint8*)this->keyframes; - delete[] c; free(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) { @@ -69,7 +96,7 @@ Animation::streamRead(Stream *stream) if(stream->readI32() != 0x100) return nil; int32 typeID = stream->readI32(); - AnimInterpolatorInfo *interpInfo = findAnimInterpolatorInfo(typeID); + AnimInterpolatorInfo *interpInfo = AnimInterpolatorInfo::find(typeID); int32 numFrames = stream->readI32(); int32 flags = stream->readI32(); float duration = stream->readF32(); @@ -82,15 +109,15 @@ Animation* Animation::streamReadLegacy(Stream *stream) { Animation *anim; - AnimInterpolatorInfo *interpInfo = findAnimInterpolatorInfo(1); + 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->read(frames[i].q, 4*4); - stream->read(frames[i].t, 3*4); + stream->read(&frames[i].q, 4*4); + stream->read(&frames[i].t, 3*4); frames[i].time = stream->readF32(); int32 prev = stream->readI32(); frames[i].prev = &frames[prev]; @@ -120,8 +147,8 @@ Animation::streamWriteLegacy(Stream *stream) assert(interpInfo->id == 1); HAnimKeyFrame *frames = (HAnimKeyFrame*)this->keyframes; for(int32 i = 0; i < this->numFrames; i++){ - stream->write(frames[i].q, 4*4); - stream->write(frames[i].t, 3*4); + stream->write(&frames[i].q, 4*4); + stream->write(&frames[i].t, 3*4); stream->writeF32(frames[i].time); stream->writeI32(frames[i].prev - frames); } @@ -136,321 +163,114 @@ Animation::streamGetSize(void) return size; } -AnimInterpolator::AnimInterpolator(Animation *anim) -{ - this->anim = anim; -} - // -// UVAnim +// AnimInterpolator // -void -UVAnimCustomData::destroy(Animation *anim) +AnimInterpolator* +AnimInterpolator::create(int32 numNodes, int32 maxFrameSize) { - this->refCount--; - if(this->refCount <= 0) - anim->destroy(); -} + AnimInterpolator *interp; + int32 sz; + int32 realsz = maxFrameSize; -UVAnimDictionary *currentUVAnimDictionary; - -UVAnimDictionary* -UVAnimDictionary::create(void) -{ - UVAnimDictionary *dict = (UVAnimDictionary*)malloc(sizeof(UVAnimDictionary)); - if(dict == nil){ - RWERROR((ERR_ALLOC, sizeof(UVAnimDictionary))); + // Add some space for pointers and padding, hopefully this will + // enough. Don't change maxFrameSize not to mess up streaming. + if(sizeof(void*) > 4) + realsz += 16; + sz = sizeof(AnimInterpolator) + numNodes*realsz; + interp = (AnimInterpolator*)malloc(sz); + if(interp == nil){ + RWERROR((ERR_ALLOC, sz)); return nil; } - dict->animations.init(); - return dict; + 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 -UVAnimDictionary::destroy(void) +AnimInterpolator::destroy(void) { - FORLIST(lnk, this->animations){ - UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk); - UVAnimCustomData *cust = (UVAnimCustomData*)de->anim->customData; - cust->destroy(de->anim); - delete de; - } free(this); } -void -UVAnimDictionary::add(Animation *anim) +bool32 +AnimInterpolator::setCurrentAnim(Animation *anim) { - UVAnimDictEntry *de = new UVAnimDictEntry; - de->anim = anim; - this->animations.append(&de->inDict); -} - -UVAnimDictionary* -UVAnimDictionary::streamRead(Stream *stream) -{ - if(!findChunk(stream, ID_STRUCT, nil, nil)){ - RWERROR((ERR_CHUNK, "STRUCT")); - return nil; + 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; } - UVAnimDictionary *dict = UVAnimDictionary::create(); - if(dict == nil) - return nil; - int32 numAnims = stream->readI32(); - Animation *anim; - for(int32 i = 0; i < numAnims; i++){ - if(!findChunk(stream, ID_ANIMANIMATION, nil, nil)){ - RWERROR((ERR_CHUNK, "ANIMANIMATION")); - goto fail; - } - anim = Animation::streamRead(stream); - if(anim == nil) - goto fail; - dict->add(anim); + 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; + this->interpCB(intf, kf1, kf2, 0.0f, anim->customData); } - return dict; -fail: - dict->destroy(); - return nil; + this->nextFrame = this->getAnimFrame(numNodes*2); + return 1; } -bool -UVAnimDictionary::streamWrite(Stream *stream) -{ - uint32 size = this->streamGetSize(); - writeChunkHeader(stream, ID_UVANIMDICT, size); - writeChunkHeader(stream, ID_STRUCT, 4); - int32 numAnims = this->count(); - stream->writeI32(numAnims); - FORLIST(lnk, this->animations){ - UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk); - de->anim->streamWrite(stream); - } - return true; -} - -uint32 -UVAnimDictionary::streamGetSize(void) -{ - uint32 size = 12 + 4; - FORLIST(lnk, this->animations){ - UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk); - size += 12 + de->anim->streamGetSize(); - } - return size; -} - -Animation* -UVAnimDictionary::find(const char *name) -{ - FORLIST(lnk, this->animations){ - Animation *anim = UVAnimDictEntry::fromDict(lnk)->anim; - UVAnimCustomData *custom = (UVAnimCustomData*)anim->customData; - if(strncmp_ci(custom->name, name, 32) == 0) - return anim; - } - return nil; -} - -static void -uvAnimStreamRead(Stream *stream, Animation *anim) -{ - UVAnimCustomData *custom = (UVAnimCustomData*)anim->customData; - UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes; - stream->readI32(); - stream->read(custom->name, 32); - stream->read(custom->nodeToUVChannel, 8*4); - custom->refCount = 1; - - for(int32 i = 0; i < anim->numFrames; i++){ - frames[i].time = stream->readF32(); - stream->read(frames[i].uv, 6*4); - int32 prev = stream->readI32(); - frames[i].prev = &frames[prev]; - } -} - -static void -uvAnimStreamWrite(Stream *stream, Animation *anim) -{ - UVAnimCustomData *custom = (UVAnimCustomData*)anim->customData; - UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes; - stream->writeI32(0); - stream->write(custom->name, 32); - stream->write(custom->nodeToUVChannel, 8*4); - - for(int32 i = 0; i < anim->numFrames; i++){ - stream->writeF32(frames[i].time); - stream->write(frames[i].uv, 6*4); - stream->writeI32(frames[i].prev - frames); - } -} - -static uint32 -uvAnimStreamGetSize(Animation *anim) -{ - return 4 + 32 + 8*4 + anim->numFrames*(4 + 6*4 + 4); -} - -static void -registerUVAnimInterpolator(void) -{ - // Linear - AnimInterpolatorInfo *info = new AnimInterpolatorInfo; - info->id = 0x1C0; - info->keyFrameSize = sizeof(UVAnimKeyFrame); - info->customDataSize = sizeof(UVAnimCustomData); - info->streamRead = uvAnimStreamRead; - info->streamWrite = uvAnimStreamWrite; - info->streamGetSize = uvAnimStreamGetSize; - registerAnimInterpolatorInfo(info); - - // Param - info = new AnimInterpolatorInfo; - info->id = 0x1C1; - info->keyFrameSize = sizeof(UVAnimKeyFrame); - info->customDataSize = sizeof(UVAnimCustomData); - info->streamRead = uvAnimStreamRead; - info->streamWrite = uvAnimStreamWrite; - info->streamGetSize = uvAnimStreamGetSize; - registerAnimInterpolatorInfo(info); -} - -int32 uvAnimOffset; - -static void* -createUVAnim(void *object, int32 offset, int32) -{ - UVAnim *uvanim; - uvanim = PLUGINOFFSET(UVAnim, object, offset); - memset(uvanim, 0, sizeof(*uvanim)); - return object; -} - -static void* -destroyUVAnim(void *object, int32 offset, int32) -{ - UVAnim *uvanim; - uvanim = PLUGINOFFSET(UVAnim, object, offset); - for(int32 i = 0; i < 8; i++){ - AnimInterpolator *ip = uvanim->interp[i]; - if(ip){ - UVAnimCustomData *custom = (UVAnimCustomData*)ip->anim->customData; - custom->destroy(ip->anim); - delete ip; - } - } - return object; -} - -static void* -copyUVAnim(void *dst, void *src, int32 offset, int32) -{ - UVAnim *srcuvanim, *dstuvanim; - dstuvanim = PLUGINOFFSET(UVAnim, dst, offset); - srcuvanim = PLUGINOFFSET(UVAnim, src, offset); - for(int32 i = 0; i < 8; i++){ - AnimInterpolator *srcip = srcuvanim->interp[i]; - AnimInterpolator *dstip; - if(srcip){ - UVAnimCustomData *custom = (UVAnimCustomData*)srcip->anim->customData; - dstip = new AnimInterpolator(srcip->anim); - custom->refCount++; - dstuvanim->interp[i] = dstip; - } - } - return dst; -} - -Animation* -makeDummyAnimation(const char *name) -{ - AnimInterpolatorInfo *interpInfo = findAnimInterpolatorInfo(0x1C0); - Animation *anim = Animation::create(interpInfo, 2, 0, 1.0f); - UVAnimCustomData *custom = (UVAnimCustomData*)anim->customData; - strncpy(custom->name, name, 32); - memset(custom->nodeToUVChannel, 0, sizeof(custom->nodeToUVChannel)); - custom->refCount = 1; - // TODO: init the frames -// UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes; - return anim; -} - -static Stream* -readUVAnim(Stream *stream, int32, void *object, int32 offset, int32) -{ - UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset); - if(!findChunk(stream, ID_STRUCT, nil, nil)){ - RWERROR((ERR_CHUNK, "STRUCT")); - return nil; - } - char name[32]; - uint32 mask = stream->readI32(); - uint32 bit = 1; - for(int32 i = 0; i < 8; i++){ - if(mask & bit){ - stream->read(name, 32); - Animation *anim = nil; - if(currentUVAnimDictionary) - anim = currentUVAnimDictionary->find(name); - if(anim == nil){ - anim = makeDummyAnimation(name); - if(currentUVAnimDictionary) - currentUVAnimDictionary->add(anim); - } - UVAnimCustomData *custom = (UVAnimCustomData*)anim->customData; - AnimInterpolator *interp = new AnimInterpolator(anim); - custom->refCount++; - uvanim->interp[i] = interp; - } - bit <<= 1; - } - return stream; -} - -static Stream* -writeUVAnim(Stream *stream, int32 size, void *object, int32 offset, int32) -{ - UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset); - writeChunkHeader(stream, ID_STRUCT, size-12); - uint32 mask = 0; - uint32 bit = 1; - for(int32 i = 0; i < 8; i++){ - if(uvanim->interp[i]) - mask |= bit; - bit <<= 1; - } - stream->writeI32(mask); - for(int32 i = 0; i < 8; i++){ - if(uvanim->interp[i]){ - UVAnimCustomData *custom = - (UVAnimCustomData*)uvanim->interp[i]->anim->customData; - stream->write(custom->name, 32); - } - } - return stream; -} - -static int32 -getSizeUVAnim(void *object, int32 offset, int32) -{ - UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset); - int32 size = 0; - for(int32 i = 0; i < 8; i++) - if(uvanim->interp[i]) - size += 32; - return size ? size + 12 + 4 : 0; -} - - void -registerUVAnimPlugin(void) +AnimInterpolator::addTime(float32 t) { - registerUVAnimInterpolator(); - uvAnimOffset = Material::registerPlugin(sizeof(UVAnim), ID_UVANIMATION, - createUVAnim, destroyUVAnim, copyUVAnim); - Material::registerPluginStream(ID_UVANIMATION, readUVAnim, writeUVAnim, getSizeUVAnim); + 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; + 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); + } } } diff --git a/src/base.cpp b/src/base.cpp index 5368358..5144d88 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -10,7 +10,6 @@ #include "rwplg.h" #include "rwpipeline.h" #include "rwobjects.h" -#include "rwplugins.h" #include "rwengine.h" namespace rw { @@ -74,6 +73,41 @@ mult(const Quat &q, const Quat &p) q.w*p.z + q.z*p.w + q.x*p.y - q.y*p.x); } +Quat +lerp(const Quat &q, const Quat &p, float32 r) +{ + float32 c; + Quat q1 = q; + c = dot(q1, p); + if(c < 0.0f){ + c = -c; + q1 = negate(q1); + } + return Quat(q1.w + r*(p.w - q1.w), + q1.x + r*(p.x - q1.x), + q1.y + r*(p.y - q1.y), + q1.z + r*(p.z - q1.z)); +}; + +Quat +slerp(const Quat &q, const Quat &p, float32 a) +{ + float32 c; + Quat q1 = q; + c = dot(q1, p); + if(c < 0.0f){ + c = -c; + q1 = negate(q1); + } + float32 phi = acos(c); + if(phi > 0.00001f){ + float32 s = sin(phi); + return add(scale(q1, sin((1.0f-a)*phi)/s), + scale(p, sin(a*phi)/s)); + } + return q1; +} + V3d cross(const V3d &a, const V3d &b) { @@ -82,19 +116,35 @@ cross(const V3d &a, const V3d &b) a.x*b.y - a.y*b.x); } +/* q must be normalized */ Matrix Matrix::makeRotation(const Quat &q) { Matrix res; - res.right.x = q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z; - res.right.y = 2*q.w*q.z + 2*q.x*q.y; - res.right.z = 2*q.x*q.z - 2*q.w*q.y; - res.up.x = 2*q.x*q.y - 2*q.w*q.z; - res.up.y = q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z; - res.up.z = 2*q.w*q.x + 2*q.y*q.z; - res.at.x = 2*q.w*q.y + 2*q.x*q.z; - res.at.y = 2*q.y*q.z - 2*q.w*q.x; - res.at.z = q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z; + float xx = q.x*q.x; + float yy = q.y*q.y; + float zz = q.z*q.z; + float yz = q.y*q.z; + float zx = q.z*q.x; + float xy = q.x*q.y; + float wx = q.w*q.x; + float wy = q.w*q.y; + float wz = q.w*q.z; + + res.right.x = 1.0f - 2.0f*(yy + zz); + res.right.y = 2.0f*(xy + wz); + res.right.z = 2.0f*(zx - wy); + + res.up.x = 2.0f*(xy - wz); + res.up.y = 1.0f - 2.0f*(xx + zz); + res.up.z = 2.0f*(yz + wx); + + res.at.x = 2.0f*(zx + wy); + res.at.y = 2.0f*(yz - wx); + res.at.z = 1.0f - 2.0f*(xx + yy); + + res.pos.x = res.pos.y = res.pos.z = 0.0f; + res.rightw = res.upw = res.atw = 0.0f; res.posw = 1.0f; return res; diff --git a/src/base.err b/src/base.err index b66bfec..52df5b2 100644 --- a/src/base.err +++ b/src/base.err @@ -1,5 +1,5 @@ ECODE(ERR_GENERAL, - "General Error") + "Error: %s") ECODE(ERR_ALLOC, "Couldn't allocate 0x%X bytes") ECODE(ERR_FILE, diff --git a/src/d3d/d3d8plugins.cpp b/src/d3d/d3d8plugins.cpp index 49dd37c..16234d9 100644 --- a/src/d3d/d3d8plugins.cpp +++ b/src/d3d/d3d8plugins.cpp @@ -8,6 +8,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #include "rwd3d.h" diff --git a/src/d3d/d3d9plugins.cpp b/src/d3d/d3d9plugins.cpp index f702478..906310d 100644 --- a/src/d3d/d3d9plugins.cpp +++ b/src/d3d/d3d9plugins.cpp @@ -8,6 +8,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #include "rwd3d.h" diff --git a/src/d3d/xbox.cpp b/src/d3d/xbox.cpp index 58c3064..2120b7c 100644 --- a/src/d3d/xbox.cpp +++ b/src/d3d/xbox.cpp @@ -9,7 +9,6 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" -#include "../rwplugins.h" #include "rwxbox.h" #include "rwxboximpl.h" diff --git a/src/d3d/xboxplugins.cpp b/src/d3d/xboxplugins.cpp index 0c9d675..2561b8f 100644 --- a/src/d3d/xboxplugins.cpp +++ b/src/d3d/xboxplugins.cpp @@ -8,6 +8,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #include "rwxbox.h" @@ -115,7 +116,7 @@ skinInstanceCB(Geometry *geo, InstanceDataHeader *header) { defaultInstanceCB(geo, header); - Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + Skin *skin = Skin::get(geo); if(skin == nil) return; NativeSkin *natskin = new NativeSkin; @@ -169,7 +170,7 @@ skinUninstanceCB(Geometry *geo, InstanceDataHeader *header) { defaultUninstanceCB(geo, header); - Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + Skin *skin = Skin::get(geo); if(skin == nil) return; NativeSkin *natskin = (NativeSkin*)skin->platformData; diff --git a/src/geoplg.cpp b/src/geoplg.cpp index b0f419b..20e1bbe 100644 --- a/src/geoplg.cpp +++ b/src/geoplg.cpp @@ -8,6 +8,7 @@ #include "rwplg.h" #include "rwpipeline.h" #include "rwobjects.h" +#include "rwanim.h" #include "rwplugins.h" #include "ps2/rwps2.h" #include "ps2/rwps2plg.h" diff --git a/src/gl/gl3.cpp b/src/gl/gl3.cpp index a63acda..eaf7b5d 100644 --- a/src/gl/gl3.cpp +++ b/src/gl/gl3.cpp @@ -9,7 +9,6 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" -#include "../rwplugins.h" #ifdef RW_OPENGL #include #endif diff --git a/src/gl/gl3driver.cpp b/src/gl/gl3driver.cpp index 94a2ea8..1e4aea2 100644 --- a/src/gl/gl3driver.cpp +++ b/src/gl/gl3driver.cpp @@ -8,7 +8,6 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" -#include "../rwplugins.h" #ifdef RW_OPENGL #include #include "rwgl3.h" diff --git a/src/gl/gl3pipe.cpp b/src/gl/gl3pipe.cpp index 84e5b68..e23eafb 100644 --- a/src/gl/gl3pipe.cpp +++ b/src/gl/gl3pipe.cpp @@ -9,7 +9,6 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" -#include "../rwplugins.h" #ifdef RW_OPENGL #include #endif diff --git a/src/gl/gl3plugins.cpp b/src/gl/gl3plugins.cpp index 281fff1..0024dc7 100644 --- a/src/gl/gl3plugins.cpp +++ b/src/gl/gl3plugins.cpp @@ -8,6 +8,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #ifdef RW_OPENGL @@ -229,10 +230,13 @@ makeMatFXPipeline(void) // Skin +Shader *skinShader; + static void* skinOpen(void *o, int32, int32) { skinGlobals.pipelines[PLATFORM_GL3] = makeSkinPipeline(); + skinShader = Shader::fromFiles("skin.vert", "simple.frag"); return o; } @@ -247,15 +251,250 @@ initSkin(void) { Driver::registerPlugin(PLATFORM_GL3, 0, ID_SKIN, skinOpen, skinClose); + registerUniform("u_boneMatrices"); +} + +enum +{ + ATTRIB_WEIGHTS = ATTRIB_TEXCOORDS7+1, + ATTRIB_INDICES +}; + +void +skinInstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + AttribDesc attribs[14], *a; + uint32 stride; + + // + // Create attribute descriptions + // + a = attribs; + stride = 0; + + // Positions + a->index = ATTRIB_POS; + a->size = 3; + a->type = GL_FLOAT; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 12; + a++; + + // Normals + // TODO: compress + bool hasNormals = !!(geo->geoflags & Geometry::NORMALS); + if(hasNormals){ + a->index = ATTRIB_NORMAL; + a->size = 3; + a->type = GL_FLOAT; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 12; + a++; + } + + // Prelighting + bool isPrelit = !!(geo->geoflags & Geometry::PRELIT); + if(isPrelit){ + a->index = ATTRIB_COLOR; + a->size = 4; + a->type = GL_UNSIGNED_BYTE; + a->normalized = GL_TRUE; + a->offset = stride; + stride += 4; + a++; + } + + // Texture coordinates + for(int32 n = 0; n < geo->numTexCoordSets; n++){ + a->index = ATTRIB_TEXCOORDS0+n; + a->size = 2; + a->type = GL_FLOAT; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 8; + a++; + } + + // Weights + a->index = ATTRIB_WEIGHTS; + a->size = 4; + a->type = GL_FLOAT; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 16; + a++; + + // Indices + a->index = ATTRIB_INDICES; + a->size = 4; + a->type = GL_UNSIGNED_BYTE; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 4; + a++; + + header->numAttribs = a - attribs; + for(a = attribs; a != &attribs[header->numAttribs]; a++) + a->stride = stride; + header->attribDesc = new AttribDesc[header->numAttribs]; + memcpy(header->attribDesc, attribs, + header->numAttribs*sizeof(AttribDesc)); + + // + // Allocate and fill vertex buffer + // + Skin *skin = Skin::get(geo); + uint8 *verts = new uint8[header->totalNumVertex*stride]; + header->vertexBuffer = verts; + + // Positions + for(a = attribs; a->index != ATTRIB_POS; a++) + ; + instV3d(VERT_FLOAT3, verts + a->offset, + geo->morphTargets[0].vertices, + header->totalNumVertex, a->stride); + + // Normals + if(hasNormals){ + for(a = attribs; a->index != ATTRIB_NORMAL; a++) + ; + instV3d(VERT_FLOAT3, verts + a->offset, + geo->morphTargets[0].normals, + header->totalNumVertex, a->stride); + } + + // Prelighting + if(isPrelit){ + for(a = attribs; a->index != ATTRIB_COLOR; a++) + ; + instColor(VERT_RGBA, verts + a->offset, + geo->colors, + header->totalNumVertex, a->stride); + } + + // Texture coordinates + for(int32 n = 0; n < geo->numTexCoordSets; n++){ + for(a = attribs; a->index != ATTRIB_TEXCOORDS0+n; a++) + ; + instV2d(VERT_FLOAT2, verts + a->offset, + geo->texCoords[n], + header->totalNumVertex, a->stride); + } + + // Weights + for(a = attribs; a->index != ATTRIB_WEIGHTS; a++) + ; + instV4d(VERT_FLOAT4, verts + a->offset, + skin->weights, + header->totalNumVertex, a->stride); + + // Indices + for(a = attribs; a->index != ATTRIB_INDICES; a++) + ; + // not really colors of course but what the heck + instColor(VERT_RGBA, verts + a->offset, + skin->indices, + header->totalNumVertex, a->stride); + + glGenBuffers(1, &header->vbo); + glBindBuffer(GL_ARRAY_BUFFER, header->vbo); + glBufferData(GL_ARRAY_BUFFER, header->totalNumVertex*stride, + header->vertexBuffer, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void +skinUninstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + assert(0 && "can't uninstance"); +} + +#define U(s) currentShader->uniformLocations[findUniform(s)] + +static float skinMatrices[64*16]; + +void +updateSkinMatrices(Atomic *a) +{ + Skin *skin = Skin::get(a->geometry); + HAnimHierarchy *hier = Skin::getHierarchy(a); + Matrix *invMats = (Matrix*)skin->inverseMatrices; + + float *m; + m = (float*)skinMatrices; + for(int i = 0; i < hier->numNodes; i++){ + invMats[i].rightw = 0.0f; + invMats[i].upw = 0.0f; + invMats[i].atw = 0.0f; + invMats[i].posw = 1.0f; + Matrix::mult((Matrix*)m, &hier->matrices[i], &invMats[i]); + m[3] = 0.0f; + m[7] = 0.0f; + m[11] = 0.0f; + m[15] = 1.0f; + m += 16; + } +} + +void +skinRenderCB(Atomic *atomic, InstanceDataHeader *header) +{ + Material *m; + RGBAf col; + GLfloat surfProps[4]; + int id; + + setWorldMatrix(atomic->getFrame()->getLTM()); + lightingCB(); + + glBindBuffer(GL_ARRAY_BUFFER, header->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo); + setAttribPointers(header); + + InstanceData *inst = header->inst; + int32 n = header->numMeshes; + + rw::setRenderState(ALPHATESTFUNC, 1); + rw::setRenderState(ALPHATESTREF, 50); + + skinShader->use(); + + updateSkinMatrices(atomic); + glUniformMatrix4fv(U("u_boneMatrices"), 64, GL_FALSE, + (GLfloat*)skinMatrices); + + while(n--){ + m = inst->material; + + convColor(&col, &m->color); + glUniform4fv(U("u_matColor"), 1, (GLfloat*)&col); + + surfProps[0] = m->surfaceProps.ambient; + surfProps[1] = m->surfaceProps.specular; + surfProps[2] = m->surfaceProps.diffuse; + surfProps[3] = 0.0f; + glUniform4fv(U("u_surfaceProps"), 1, surfProps); + + setTexture(0, m->texture); + + rw::setRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); + + flushCache(); + glDrawElements(header->primType, inst->numIndex, + GL_UNSIGNED_SHORT, (void*)(uintptr)inst->offset); + inst++; + } } ObjPipeline* makeSkinPipeline(void) { ObjPipeline *pipe = new ObjPipeline(PLATFORM_GL3); - pipe->instanceCB = defaultInstanceCB; - pipe->uninstanceCB = defaultUninstanceCB; - pipe->renderCB = defaultRenderCB; + pipe->instanceCB = skinInstanceCB; + pipe->uninstanceCB = skinUninstanceCB; + pipe->renderCB = skinRenderCB; pipe->pluginID = ID_SKIN; pipe->pluginData = 1; return pipe; diff --git a/src/gl/gl3raster.cpp b/src/gl/gl3raster.cpp index 99032a5..2e0f50d 100644 --- a/src/gl/gl3raster.cpp +++ b/src/gl/gl3raster.cpp @@ -9,7 +9,6 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" -#include "../rwplugins.h" #ifdef RW_OPENGL #include #endif diff --git a/src/gl/gl3render.cpp b/src/gl/gl3render.cpp index b5048b8..bb3b2c6 100644 --- a/src/gl/gl3render.cpp +++ b/src/gl/gl3render.cpp @@ -8,7 +8,6 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" -#include "../rwplugins.h" #ifdef RW_OPENGL #include #include "rwgl3.h" diff --git a/src/gl/gl3shader.cpp b/src/gl/gl3shader.cpp index afcba7d..b210e4e 100644 --- a/src/gl/gl3shader.cpp +++ b/src/gl/gl3shader.cpp @@ -8,7 +8,6 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" -#include "../rwplugins.h" #ifdef RW_OPENGL #include #include "rwgl3.h" diff --git a/src/gl/wdgl.cpp b/src/gl/wdgl.cpp index c4a1d74..748b211 100644 --- a/src/gl/wdgl.cpp +++ b/src/gl/wdgl.cpp @@ -8,6 +8,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #include "rwwdgl.h" @@ -603,7 +604,7 @@ skinInstanceCB(Geometry *g, int32 i, uint32 offset) header->dataSize = offset*g->numVertices; header->data = new uint8[header->dataSize]; - Skin *skin = *PLUGINOFFSET(Skin*, g, skinGlobals.offset); + Skin *skin = Skin::get(g); if(skin == nil) return 8; @@ -631,7 +632,7 @@ skinUninstanceCB(Geometry *geo) { InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; - Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + Skin *skin = Skin::get(geo); if(skin == nil) return; diff --git a/src/hanim.cpp b/src/hanim.cpp index a7ff37c..17de569 100644 --- a/src/hanim.cpp +++ b/src/hanim.cpp @@ -8,6 +8,7 @@ #include "rwplg.h" #include "rwpipeline.h" #include "rwobjects.h" +#include "rwanim.h" #include "rwplugins.h" #include "ps2/rwps2.h" #include "ps2/rwps2plg.h" @@ -25,22 +26,24 @@ int32 hAnimOffset; bool32 hAnimDoStream = 1; HAnimHierarchy* -HAnimHierarchy::create(int32 numNodes, int32 *nodeFlags, int32 *nodeIDs, int32 flags, int32 maxKeySize) +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->maxInterpKeyFrameSize = maxKeySize; hier->parentFrame = nil; hier->parentHierarchy = hier; - if(hier->flags & 2) - hier->matrices = hier->matricesUnaligned = nil; - else{ + if(hier->flags & NOMATRICES){ + hier->matrices = nil; + hier->matricesUnaligned = nil; + }else{ hier->matricesUnaligned = - (float*) new uint8[hier->numNodes*64 + 15]; + (void*) new uint8[hier->numNodes*64 + 15]; hier->matrices = - (float*)((uintptr)hier->matricesUnaligned & ~0xF); + (Matrix*)((uintptr)hier->matricesUnaligned & ~0xF); } hier->nodeInfo = new HAnimNodeInfo[hier->numNodes]; for(int32 i = 0; i < hier->numNodes; i++){ @@ -112,6 +115,43 @@ HAnimHierarchy::find(Frame *f) 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) { @@ -161,6 +201,8 @@ readHAnim(Stream *stream, int32, void *object, int32 offset, int32) 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++){ @@ -190,7 +232,7 @@ writeHAnim(Stream *stream, int32, void *object, int32 offset, int32) HAnimHierarchy *hier = hanim->hierarchy; stream->writeI32(hier->numNodes); stream->writeI32(hier->flags); - stream->writeI32(hier->maxInterpKeyFrameSize); + 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); @@ -217,9 +259,9 @@ 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(); + stream->read(&frames[i].q, 4*4); + stream->read(&frames[i].t, 3*4); + int32 prev = stream->readI32()/0x24; frames[i].prev = &frames[prev]; } } @@ -230,9 +272,9 @@ 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); + stream->write(&frames[i].q, 4*4); + stream->write(&frames[i].t, 3*4); + stream->writeI32((frames[i].prev - frames)*0x24); } } @@ -242,6 +284,31 @@ 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 = Matrix::makeRotation(f->q); + 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) { @@ -255,12 +322,18 @@ registerHAnimPlugin(void) AnimInterpolatorInfo *info = new AnimInterpolatorInfo; info->id = 1; - info->keyFrameSize = sizeof(HAnimKeyFrame); - info->customDataSize = sizeof(HAnimKeyFrame); + 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; - registerAnimInterpolatorInfo(info); + AnimInterpolatorInfo::registerInterp(info); } } diff --git a/src/matfx.cpp b/src/matfx.cpp index 537d7ee..3861db7 100644 --- a/src/matfx.cpp +++ b/src/matfx.cpp @@ -9,6 +9,7 @@ #include "rwpipeline.h" #include "rwobjects.h" #include "rwengine.h" +#include "rwanim.h" #include "rwplugins.h" #include "ps2/rwps2.h" #include "ps2/rwps2plg.h" diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 691e9f5..d30d1cd 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -51,6 +51,19 @@ findMinVertAndNumVertices(uint16 *indices, uint32 numIndices, uint32 *minVert, i *numVertices = max - min + 1; } +void +instV4d(int type, uint8 *dst, float *src, uint32 numVertices, uint32 stride) +{ + if(type == VERT_FLOAT4) + for(uint32 i = 0; i < numVertices; i++){ + memcpy(dst, src, 16); + dst += stride; + src += 4; + } + else + assert(0 && "unsupported instV3d type"); +} + void instV3d(int type, uint8 *dst, float *src, uint32 numVertices, uint32 stride) { diff --git a/src/ps2/pds.cpp b/src/ps2/pds.cpp index a1596c1..346d0e8 100644 --- a/src/ps2/pds.cpp +++ b/src/ps2/pds.cpp @@ -7,6 +7,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwplugins.h" #include "rwps2.h" #include "rwps2plg.h" diff --git a/src/ps2/ps2.cpp b/src/ps2/ps2.cpp index 889a4de..a31e033 100644 --- a/src/ps2/ps2.cpp +++ b/src/ps2/ps2.cpp @@ -9,6 +9,7 @@ #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" +#include "../rwanim.h" #include "../rwplugins.h" #include "rwps2.h" #include "rwps2plg.h" @@ -947,8 +948,8 @@ genericUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh uint32 *weights = nil; int8 *adc = nil; Skin *skin = nil; - if(skinGlobals.offset) - skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + if(skinGlobals.geoOffset) + skin = Skin::get(geo); PipeAttribute *a; for(int32 i = 0; i < nelem(pipe->attribs); i++) diff --git a/src/ps2/ps2matfx.cpp b/src/ps2/ps2matfx.cpp index f81e855..5908974 100644 --- a/src/ps2/ps2matfx.cpp +++ b/src/ps2/ps2matfx.cpp @@ -8,6 +8,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #include "rwps2.h" diff --git a/src/ps2/ps2skin.cpp b/src/ps2/ps2skin.cpp index 2e2c46c..c0b09c7 100644 --- a/src/ps2/ps2skin.cpp +++ b/src/ps2/ps2skin.cpp @@ -8,6 +8,7 @@ #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" +#include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #include "rwps2.h" @@ -183,7 +184,7 @@ instanceSkinData(Geometry*, Mesh *m, Skin *skin, uint32 *data) void skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data) { - Skin *skin = *PLUGINOFFSET(Skin*, g, skinGlobals.offset); + Skin *skin = Skin::get(g); if(skin == nil) return; instanceSkinData(g, m, skin, (uint32*)data[4]); @@ -193,7 +194,7 @@ skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data) int32 findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v) { - Skin *skin = *PLUGINOFFSET(Skin*, g, skinGlobals.offset); + Skin *skin = Skin::get(g); float32 *wghts = nil; uint8 *inds = nil; if(skin){ @@ -245,7 +246,7 @@ findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v) void insertVertexSkin(Geometry *geo, int32 i, uint32 mask, Vertex *v) { - Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + Skin *skin = Skin::get(geo); insertVertex(geo, i, mask, v); if(mask & 0x10000){ memcpy(&skin->weights[i*4], v->w, 16); @@ -308,7 +309,7 @@ skinUninstanceCB(MatPipeline*, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 void skinPreCB(MatPipeline*, Geometry *geo) { - Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + Skin *skin = Skin::get(geo); if(skin == nil) return; uint8 *data = skin->data; @@ -322,7 +323,7 @@ skinPreCB(MatPipeline*, Geometry *geo) void skinPostCB(MatPipeline*, Geometry *geo) { - Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + Skin *skin = Skin::get(geo); if(skin){ skin->findNumWeights(geo->numVertices); skin->findUsedBones(geo->numVertices); diff --git a/src/rwanim.h b/src/rwanim.h new file mode 100644 index 0000000..dad4be7 --- /dev/null +++ b/src/rwanim.h @@ -0,0 +1,174 @@ +#include + +namespace rw { + +struct Animation; + +// These sizes of these are sadly not platform independent +// because pointer sizes can vary. + +struct KeyFrameHeader +{ + KeyFrameHeader *prev; + float32 time; + + KeyFrameHeader *next(int32 sz){ + return (KeyFrameHeader*)((uint8*)this + sz); } +}; + +struct InterpFrameHeader +{ + KeyFrameHeader *keyFrame1; + KeyFrameHeader *keyFrame2; +}; + +struct AnimInterpolatorInfo +{ + typedef void (*ApplyCB)(void *result, void *frame); + typedef void (*BlendCB)(void *out, void *in1, void *in2, float32 a); + typedef void (*InterpCB)(void *out, void *in1, void *in2, float32 t, + void *custom); + typedef void (*AddCB)(void *out, void *in1, void *in2); + typedef void (*MulRecipCB)(void *frame, void *start); + + int32 id; + int32 interpKeyFrameSize; + int32 animKeyFrameSize; + int32 customDataSize; + + ApplyCB applyCB; + BlendCB blendCB; + InterpCB interpCB; + AddCB addCB; + MulRecipCB mulRecipCB; + void (*streamRead)(Stream *stream, Animation *anim); + void (*streamWrite)(Stream *stream, Animation *anim); + uint32 (*streamGetSize)(Animation *anim); + + static void registerInterp(AnimInterpolatorInfo *interpInfo); + static AnimInterpolatorInfo *find(int32 id); +}; + +struct Animation +{ + AnimInterpolatorInfo *interpInfo; + int32 numFrames; + int32 flags; + float32 duration; + void *keyframes; + void *customData; + + static Animation *create(AnimInterpolatorInfo*, int32 numFrames, + int32 flags, float duration); + void destroy(void); + int32 getNumNodes(void); + static Animation *streamRead(Stream *stream); + static Animation *streamReadLegacy(Stream *stream); + bool streamWrite(Stream *stream); + bool streamWriteLegacy(Stream *stream); + uint32 streamGetSize(void); +}; + +struct AnimInterpolator +{ + Animation *currentAnim; + float32 currentTime; + void *nextFrame; + int32 maxInterpKeyFrameSize; + int32 currentInterpKeyFrameSize; + int32 currentAnimKeyFrameSize; + int32 numNodes; + // TODO some callbacks, parent/sub + // cached from the InterpolatorInfo + AnimInterpolatorInfo::ApplyCB applyCB; + AnimInterpolatorInfo::BlendCB blendCB; + AnimInterpolatorInfo::InterpCB interpCB; + AnimInterpolatorInfo::AddCB addCB; + // after this interpolated frames + + static AnimInterpolator *create(int32 numNodes, int32 maxKeyFrameSize); + void destroy(void); + bool32 setCurrentAnim(Animation *anim); + void addTime(float32 t); + void *getFrames(void){ return this+1;} + InterpFrameHeader *getInterpFrame(int32 n){ + return (InterpFrameHeader*)((uint8*)getFrames() + + n*currentInterpKeyFrameSize); + } + KeyFrameHeader *getAnimFrame(int32 n){ + return (KeyFrameHeader*)((uint8*)currentAnim->keyframes + + n*currentAnimKeyFrameSize); + } +}; + +// +// UV anim +// + +struct UVAnimKeyFrame +{ + UVAnimKeyFrame *prev; + float32 time; + float32 uv[6]; +}; + +struct UVAnimInterpFrame +{ + UVAnimKeyFrame *keyFrame1; + UVAnimKeyFrame *keyFrame2; + float32 uv[6]; +}; + +struct UVAnimDictionary; + +// RW does it differently...maybe we should implement RtDict +// and make it more general? + +struct UVAnimCustomData +{ + char name[32]; + int32 nodeToUVChannel[8]; + int32 refCount; + + void destroy(Animation *anim); + static UVAnimCustomData *get(Animation *anim){ + return (UVAnimCustomData*)anim->customData; } +}; + +// This should be more general probably +struct UVAnimDictEntry +{ + Animation *anim; + LLLink inDict; + static UVAnimDictEntry *fromDict(LLLink *lnk){ + return LLLinkGetData(lnk, UVAnimDictEntry, inDict); } +}; + +// This too +struct UVAnimDictionary +{ + LinkList animations; + + static UVAnimDictionary *create(void); + void destroy(void); + int32 count(void) { return this->animations.count(); } + void add(Animation *anim); + Animation *find(const char *name); + + static UVAnimDictionary *streamRead(Stream *stream); + bool streamWrite(Stream *stream); + uint32 streamGetSize(void); +}; + +extern UVAnimDictionary *currentUVAnimDictionary; + +struct UVAnim +{ + AnimInterpolator *interp[8]; +}; + +extern int32 uvAnimOffset; + +void registerUVAnimPlugin(void); + +} diff --git a/src/rwbase.h b/src/rwbase.h index 106de32..6432282 100644 --- a/src/rwbase.h +++ b/src/rwbase.h @@ -112,14 +112,20 @@ inline V3d normalize(const V3d &v) { return scale(v, 1.0f/length(v)); } inline V3d setlength(const V3d &v, float32 l) { return scale(v, l/length(v)); } V3d cross(const V3d &a, const V3d &b); inline float32 dot(const V3d &a, const V3d &b) { return a.x*b.x + a.y*b.y + a.z*b.z; } +inline V3d lerp(const V3d &a, const V3d &b, float32 r){ + return V3d(a.x + r*(b.x - a.x), + a.y + r*(b.y - a.y), + a.z + r*(b.z - a.z)); +}; struct Quat { - float32 w, x, y, z; - Quat(void) : w(0.0f), x(0.0f), y(0.0f), z(0.0f) {} - Quat(float32 w, float32 x, float32 y, float32 z) : w(w), x(x), y(y), z(z) {} - Quat(float32 w, V3d vec) : w(w), x(vec.x), y(vec.y), z(vec.z) {} - Quat(V3d vec) : w(0.0f), x(vec.x), y(vec.y), z(vec.z) {} + // order is important for streaming + float32 x, y, z, w; + Quat(void) : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {} + Quat(float32 w, float32 x, float32 y, float32 z) : x(x), y(y), z(z), w(w) {} + Quat(float32 w, V3d vec) : x(vec.x), y(vec.y), z(vec.z), w(w) {} + Quat(V3d vec) : x(vec.x), y(vec.y), z(vec.z), w(0.0f) {} static Quat rotation(float32 angle, const V3d &axis){ return Quat(cos(angle/2.0f), scale(axis, sin(angle/2.0f))); } void set(float32 w, float32 x, float32 y, float32 z){ @@ -129,12 +135,17 @@ struct Quat inline Quat add(const Quat &q, const Quat &p) { return Quat(q.w+p.w, q.x+p.x, q.y+p.y, q.z+p.z); } inline Quat sub(const Quat &q, const Quat &p) { return Quat(q.w-p.w, q.x-p.x, q.y-p.y, q.z-p.z); } +inline Quat negate(const Quat &q) { return Quat(-q.w, -q.x, -q.y, -q.z); } +inline float32 dot(const Quat &q, const Quat &p) { return q.w*p.w + q.x*p.x + q.y*p.y + q.z*p.z; } inline Quat scale(const Quat &q, float32 r) { return Quat(q.w*r, q.x*r, q.y*r, q.z*r); } inline float32 length(const Quat &q) { return sqrt(q.w*q.w + q.x*q.x + q.y*q.y + q.z*q.z); } inline Quat normalize(const Quat &q) { return scale(q, 1.0f/length(q)); } inline Quat conj(const Quat &q) { return Quat(q.w, -q.x, -q.y, -q.z); } Quat mult(const Quat &q, const Quat &p); inline V3d rotate(const V3d &v, const Quat &q) { return mult(mult(q, Quat(v)), conj(q)).vec(); } +Quat lerp(const Quat &q, const Quat &p, float32 r); +Quat slerp(const Quat &q, const Quat &p, float32 a); + struct Matrix { diff --git a/src/rwobjects.h b/src/rwobjects.h index 3aabeb8..dbc8e0e 100644 --- a/src/rwobjects.h +++ b/src/rwobjects.h @@ -641,102 +641,4 @@ struct TexDictionary : PluginBase extern TexDictionary *currentTexDictionary; -struct Animation; - -struct AnimInterpolatorInfo -{ - int32 id; - int32 keyFrameSize; - int32 customDataSize; - void (*streamRead)(Stream *stream, Animation *anim); - void (*streamWrite)(Stream *stream, Animation *anim); - uint32 (*streamGetSize)(Animation *anim); -}; - -void registerAnimInterpolatorInfo(AnimInterpolatorInfo *interpInfo); -AnimInterpolatorInfo *findAnimInterpolatorInfo(int32 id); - -struct Animation -{ - AnimInterpolatorInfo *interpInfo; - int32 numFrames; - int32 flags; - float duration; - void *keyframes; - void *customData; - - static Animation *create(AnimInterpolatorInfo*, int32 numFrames, int32 flags, float duration); - void destroy(void); - static Animation *streamRead(Stream *stream); - static Animation *streamReadLegacy(Stream *stream); - bool streamWrite(Stream *stream); - bool streamWriteLegacy(Stream *stream); - uint32 streamGetSize(void); -}; - -struct AnimInterpolator -{ - // only a stub right now - Animation *anim; - - AnimInterpolator(Animation *anim); -}; - -struct UVAnimKeyFrame -{ - UVAnimKeyFrame *prev; - float time; - float uv[6]; -}; - -struct UVAnimDictionary; - -// RW does it differently...maybe we should implement RtDict -// and make it more general? - -struct UVAnimCustomData -{ - char name[32]; - int32 nodeToUVChannel[8]; - int32 refCount; - - void destroy(Animation *anim); -}; - -// This should be more general probably -struct UVAnimDictEntry -{ - Animation *anim; - LLLink inDict; - static UVAnimDictEntry *fromDict(LLLink *lnk){ - return LLLinkGetData(lnk, UVAnimDictEntry, inDict); } -}; - -// This too -struct UVAnimDictionary -{ - LinkList animations; - - static UVAnimDictionary *create(void); - void destroy(void); - int32 count(void) { return this->animations.count(); } - void add(Animation *anim); - Animation *find(const char *name); - - static UVAnimDictionary *streamRead(Stream *stream); - bool streamWrite(Stream *stream); - uint32 streamGetSize(void); -}; - -extern UVAnimDictionary *currentUVAnimDictionary; - -struct UVAnim -{ - AnimInterpolator *interp[8]; -}; - -extern int32 uvAnimOffset; - -void registerUVAnimPlugin(void); - } diff --git a/src/rwpipeline.h b/src/rwpipeline.h index 870c5bc..644a0f5 100644 --- a/src/rwpipeline.h +++ b/src/rwpipeline.h @@ -41,11 +41,13 @@ enum { VERT_NORMSHORT3, VERT_FLOAT2, VERT_FLOAT3, + VERT_FLOAT4, VERT_ARGB, VERT_RGBA, VERT_COMPNORM }; +void instV4d(int type, uint8 *dst, float *src, uint32 numVertices, uint32 stride); void instV3d(int type, uint8 *dst, float *src, uint32 numVertices, uint32 stride); void uninstV3d(int type, float *dst, uint8 *src, uint32 numVertices, uint32 stride); void instV2d(int type, uint8 *dst, float *src, uint32 numVertices, uint32 stride); diff --git a/src/rwplugins.h b/src/rwplugins.h index b5d1a8e..00a2feb 100644 --- a/src/rwplugins.h +++ b/src/rwplugins.h @@ -7,9 +7,17 @@ namespace rw { struct HAnimKeyFrame { HAnimKeyFrame *prev; - float time; - float q[4]; - float t[3]; + float32 time; + Quat q; + V3d t; +}; + +struct HAnimInterpFrame +{ + HAnimKeyFrame *keyFrame1; + HAnimKeyFrame *keyFrame2; + Quat q; + V3d t; }; struct HAnimNodeInfo @@ -24,24 +32,34 @@ struct HAnimHierarchy { int32 flags; int32 numNodes; - float *matrices; - float *matricesUnaligned; + Matrix *matrices; + void *matricesUnaligned; HAnimNodeInfo *nodeInfo; Frame *parentFrame; HAnimHierarchy *parentHierarchy; // mostly unused + AnimInterpolator *currentAnim; - // temporary - int32 maxInterpKeyFrameSize; - - static HAnimHierarchy *create(int32 numNodes, int32 *nodeFlags, int32 *nodeIDs, int32 flags, int32 maxKeySize); + static HAnimHierarchy *create(int32 numNodes, int32 *nodeFlags, + int32 *nodeIDs, int32 flags, int32 maxKeySize); void destroy(void); void attachByIndex(int32 id); void attach(void); int32 getIndex(int32 id); + void updateMatrices(void); static HAnimHierarchy *get(Frame *f); + static HAnimHierarchy *get(Clump *c){ + return find(c->getFrame()); } static HAnimHierarchy *find(Frame *f); + enum Flags { + SUBHIERARCHY = 0x1, + NOMATRICES = 0x2, + + UPDATEMODELLINGMATRICES = 0x1000, + UPDATELTMS = 0x2000, + LOCALSPACEMATRICES = 0x4000 + }; enum NodeFlag { POP = 1, PUSH @@ -138,6 +156,14 @@ void registerMatFXPlugin(void); * Skin */ +struct SkinGlobals +{ + int32 geoOffset; + int32 atomicOffset; + ObjPipeline *pipelines[NUM_PLATFORMS]; +}; +extern SkinGlobals skinGlobals; + struct Skin { int32 numBones; @@ -173,15 +199,21 @@ struct Skin void init(int32 numBones, int32 numUsedBones, int32 numVertices); void findNumWeights(int32 numVertices); void findUsedBones(int32 numVertices); + static void setPipeline(Atomic *a, int32 type); + static Skin *get(Geometry *geo){ + return *PLUGINOFFSET(Skin*, geo, skinGlobals.geoOffset); + } + static void setHierarchy(Atomic *atomic, HAnimHierarchy *hier){ + *PLUGINOFFSET(HAnimHierarchy*, atomic, + skinGlobals.atomicOffset) = hier; + } + static HAnimHierarchy *getHierarchy(Atomic *atomic){ + return *PLUGINOFFSET(HAnimHierarchy*, atomic, + skinGlobals.atomicOffset); + } }; -struct SkinGlobals -{ - int32 offset; - ObjPipeline *pipelines[NUM_PLATFORMS]; -}; -extern SkinGlobals skinGlobals; Stream *readSkinSplitData(Stream *stream, Skin *skin); Stream *writeSkinSplitData(Stream *stream, Skin *skin); int32 skinSplitDataSize(Skin *skin); diff --git a/src/skin.cpp b/src/skin.cpp index a58c519..0eb4bd6 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -8,6 +8,7 @@ #include "rwplg.h" #include "rwpipeline.h" #include "rwobjects.h" +#include "rwanim.h" #include "rwengine.h" #include "rwplugins.h" #include "ps2/rwps2.h" @@ -23,7 +24,7 @@ namespace rw { -SkinGlobals skinGlobals = { 0, { nil } }; +SkinGlobals skinGlobals = { 0, 0, { nil } }; static void* createSkin(void *object, int32 offset, int32) @@ -257,6 +258,26 @@ skinRights(void *object, int32, int32, uint32) Skin::setPipeline((Atomic*)object, 1); } +static void* +createSkinAtm(void *object, int32 offset, int32) +{ + *PLUGINOFFSET(void*, object, offset) = nil; + return object; +} + +static void* +destroySkinAtm(void *object, int32 offset, int32) +{ + return object; +} + +static void* +copySkinAtm(void *dst, void *src, int32 offset, int32) +{ + *PLUGINOFFSET(void*, dst, offset) = *PLUGINOFFSET(void*, src, offset); + return dst; +} + void registerSkinPlugin(void) { @@ -274,13 +295,15 @@ registerSkinPlugin(void) wdgl::initSkin(); gl3::initSkin(); - skinGlobals.offset = Geometry::registerPlugin(sizeof(Skin*), ID_SKIN, - createSkin, - destroySkin, - copySkin); + int32 o; + o = Geometry::registerPlugin(sizeof(Skin*), ID_SKIN, + createSkin, destroySkin, copySkin); Geometry::registerPluginStream(ID_SKIN, readSkin, writeSkin, getSizeSkin); - Atomic::registerPlugin(0, ID_SKIN, nil, nil, nil); + skinGlobals.geoOffset = o; + o = Atomic::registerPlugin(sizeof(HAnimHierarchy*),ID_SKIN, + createSkinAtm, destroySkinAtm, copySkinAtm); + skinGlobals.atomicOffset = o; Atomic::setStreamRightsCallback(ID_SKIN, skinRights); } diff --git a/src/uvanim.cpp b/src/uvanim.cpp new file mode 100644 index 0000000..2e5460b --- /dev/null +++ b/src/uvanim.cpp @@ -0,0 +1,350 @@ +#include +#include +#include +#include + +#include "rwbase.h" +#include "rwerror.h" +#include "rwplg.h" +#include "rwpipeline.h" +#include "rwobjects.h" +#include "rwanim.h" +#include "rwplugins.h" + +#define PLUGIN_ID ID_UVANIMATION + +namespace rw { + +// +// UVAnim +// + +void +UVAnimCustomData::destroy(Animation *anim) +{ + this->refCount--; + if(this->refCount <= 0) + anim->destroy(); +} + +UVAnimDictionary *currentUVAnimDictionary; + +UVAnimDictionary* +UVAnimDictionary::create(void) +{ + UVAnimDictionary *dict = (UVAnimDictionary*)malloc(sizeof(UVAnimDictionary)); + if(dict == nil){ + RWERROR((ERR_ALLOC, sizeof(UVAnimDictionary))); + return nil; + } + dict->animations.init(); + return dict; +} + +void +UVAnimDictionary::destroy(void) +{ + FORLIST(lnk, this->animations){ + UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk); + UVAnimCustomData *cust = UVAnimCustomData::get(de->anim); + cust->destroy(de->anim); + delete de; + } + free(this); +} + +void +UVAnimDictionary::add(Animation *anim) +{ + UVAnimDictEntry *de = new UVAnimDictEntry; + de->anim = anim; + this->animations.append(&de->inDict); +} + +UVAnimDictionary* +UVAnimDictionary::streamRead(Stream *stream) +{ + if(!findChunk(stream, ID_STRUCT, nil, nil)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + UVAnimDictionary *dict = UVAnimDictionary::create(); + if(dict == nil) + return nil; + int32 numAnims = stream->readI32(); + Animation *anim; + for(int32 i = 0; i < numAnims; i++){ + if(!findChunk(stream, ID_ANIMANIMATION, nil, nil)){ + RWERROR((ERR_CHUNK, "ANIMANIMATION")); + goto fail; + } + anim = Animation::streamRead(stream); + if(anim == nil) + goto fail; + dict->add(anim); + } + return dict; +fail: + dict->destroy(); + return nil; +} + +bool +UVAnimDictionary::streamWrite(Stream *stream) +{ + uint32 size = this->streamGetSize(); + writeChunkHeader(stream, ID_UVANIMDICT, size); + writeChunkHeader(stream, ID_STRUCT, 4); + int32 numAnims = this->count(); + stream->writeI32(numAnims); + FORLIST(lnk, this->animations){ + UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk); + de->anim->streamWrite(stream); + } + return true; +} + +uint32 +UVAnimDictionary::streamGetSize(void) +{ + uint32 size = 12 + 4; + FORLIST(lnk, this->animations){ + UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk); + size += 12 + de->anim->streamGetSize(); + } + return size; +} + +Animation* +UVAnimDictionary::find(const char *name) +{ + FORLIST(lnk, this->animations){ + Animation *anim = UVAnimDictEntry::fromDict(lnk)->anim; + UVAnimCustomData *custom = UVAnimCustomData::get(anim); + if(strncmp_ci(custom->name, name, 32) == 0) + return anim; + } + return nil; +} + +static void +uvAnimStreamRead(Stream *stream, Animation *anim) +{ + UVAnimCustomData *custom = UVAnimCustomData::get(anim); + UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes; + stream->readI32(); + stream->read(custom->name, 32); + stream->read(custom->nodeToUVChannel, 8*4); + custom->refCount = 1; + + for(int32 i = 0; i < anim->numFrames; i++){ + frames[i].time = stream->readF32(); + stream->read(frames[i].uv, 6*4); + int32 prev = stream->readI32(); + frames[i].prev = &frames[prev]; + } +} + +static void +uvAnimStreamWrite(Stream *stream, Animation *anim) +{ + UVAnimCustomData *custom = UVAnimCustomData::get(anim); + UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes; + stream->writeI32(0); + stream->write(custom->name, 32); + stream->write(custom->nodeToUVChannel, 8*4); + + for(int32 i = 0; i < anim->numFrames; i++){ + stream->writeF32(frames[i].time); + stream->write(frames[i].uv, 6*4); + stream->writeI32(frames[i].prev - frames); + } +} + +static uint32 +uvAnimStreamGetSize(Animation *anim) +{ + return 4 + 32 + 8*4 + anim->numFrames*(4 + 6*4 + 4); +} + +static void +registerUVAnimInterpolator(void) +{ + // Linear + AnimInterpolatorInfo *info = new AnimInterpolatorInfo; + info->id = 0x1C0; + info->interpKeyFrameSize = sizeof(UVAnimInterpFrame); + info->animKeyFrameSize = sizeof(UVAnimKeyFrame); + info->customDataSize = sizeof(UVAnimCustomData); + info->applyCB = nil; + info->blendCB = nil; + info->interpCB = nil; + info->addCB = nil; + info->mulRecipCB = nil; + info->streamRead = uvAnimStreamRead; + info->streamWrite = uvAnimStreamWrite; + info->streamGetSize = uvAnimStreamGetSize; + AnimInterpolatorInfo::registerInterp(info); + + // Param + info = new AnimInterpolatorInfo; + info->id = 0x1C1; + info->interpKeyFrameSize = sizeof(UVAnimInterpFrame); + info->animKeyFrameSize = sizeof(UVAnimKeyFrame); + info->customDataSize = sizeof(UVAnimCustomData); + info->applyCB = nil; + info->blendCB = nil; + info->interpCB = nil; + info->addCB = nil; + info->mulRecipCB = nil; + info->streamRead = uvAnimStreamRead; + info->streamWrite = uvAnimStreamWrite; + info->streamGetSize = uvAnimStreamGetSize; + AnimInterpolatorInfo::registerInterp(info); +} + +int32 uvAnimOffset; + +static void* +createUVAnim(void *object, int32 offset, int32) +{ + UVAnim *uvanim; + uvanim = PLUGINOFFSET(UVAnim, object, offset); + memset(uvanim, 0, sizeof(*uvanim)); + return object; +} + +static void* +destroyUVAnim(void *object, int32 offset, int32) +{ + UVAnim *uvanim; + uvanim = PLUGINOFFSET(UVAnim, object, offset); + for(int32 i = 0; i < 8; i++){ + AnimInterpolator *ip = uvanim->interp[i]; + if(ip){ + UVAnimCustomData *custom = + UVAnimCustomData::get(ip->currentAnim); + custom->destroy(ip->currentAnim); + delete ip; + } + } + return object; +} + +static void* +copyUVAnim(void *dst, void *src, int32 offset, int32) +{ + UVAnim *srcuvanim, *dstuvanim; + dstuvanim = PLUGINOFFSET(UVAnim, dst, offset); + srcuvanim = PLUGINOFFSET(UVAnim, src, offset); + for(int32 i = 0; i < 8; i++){ + AnimInterpolator *srcip = srcuvanim->interp[i]; + AnimInterpolator *dstip; + if(srcip){ + Animation *anim = srcip->currentAnim; + UVAnimCustomData *custom = UVAnimCustomData::get(anim); + dstip = AnimInterpolator::create(anim->getNumNodes(), + anim->interpInfo->interpKeyFrameSize); + dstip->setCurrentAnim(anim); + custom->refCount++; + dstuvanim->interp[i] = dstip; + } + } + return dst; +} + +Animation* +makeDummyAnimation(const char *name) +{ + AnimInterpolatorInfo *interpInfo = AnimInterpolatorInfo::find(0x1C0); + Animation *anim = Animation::create(interpInfo, 2, 0, 1.0f); + UVAnimCustomData *custom = UVAnimCustomData::get(anim); + strncpy(custom->name, name, 32); + memset(custom->nodeToUVChannel, 0, sizeof(custom->nodeToUVChannel)); + custom->refCount = 1; + // TODO: init the frames +// UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes; + return anim; +} + +static Stream* +readUVAnim(Stream *stream, int32, void *object, int32 offset, int32) +{ + UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset); + if(!findChunk(stream, ID_STRUCT, nil, nil)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + char name[32]; + uint32 mask = stream->readI32(); + uint32 bit = 1; + for(int32 i = 0; i < 8; i++){ + if(mask & bit){ + stream->read(name, 32); + Animation *anim = nil; + if(currentUVAnimDictionary) + anim = currentUVAnimDictionary->find(name); + if(anim == nil){ + anim = makeDummyAnimation(name); + if(currentUVAnimDictionary) + currentUVAnimDictionary->add(anim); + } + UVAnimCustomData *custom = UVAnimCustomData::get(anim); + AnimInterpolator *interp; + interp = AnimInterpolator::create(anim->getNumNodes(), + anim->interpInfo->interpKeyFrameSize); + interp->setCurrentAnim(anim); + custom->refCount++; + uvanim->interp[i] = interp; + } + bit <<= 1; + } + return stream; +} + +static Stream* +writeUVAnim(Stream *stream, int32 size, void *object, int32 offset, int32) +{ + UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset); + writeChunkHeader(stream, ID_STRUCT, size-12); + uint32 mask = 0; + uint32 bit = 1; + for(int32 i = 0; i < 8; i++){ + if(uvanim->interp[i]) + mask |= bit; + bit <<= 1; + } + stream->writeI32(mask); + for(int32 i = 0; i < 8; i++){ + if(uvanim->interp[i]){ + UVAnimCustomData *custom = + UVAnimCustomData::get(uvanim->interp[i]->currentAnim); + stream->write(custom->name, 32); + } + } + return stream; +} + +static int32 +getSizeUVAnim(void *object, int32 offset, int32) +{ + UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset); + int32 size = 0; + for(int32 i = 0; i < 8; i++) + if(uvanim->interp[i]) + size += 32; + return size ? size + 12 + 4 : 0; +} + + +void +registerUVAnimPlugin(void) +{ + registerUVAnimInterpolator(); + uvAnimOffset = Material::registerPlugin(sizeof(UVAnim), ID_UVANIMATION, + createUVAnim, destroyUVAnim, copyUVAnim); + Material::registerPluginStream(ID_UVANIMATION, + readUVAnim, writeUVAnim, getSizeUVAnim); +} + +}