/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */ #include "StdH.h" #include #include #include #include #include #include #include #include #include #include #include #define ANIMSET_VERSION 14 #define ANIMSET_ID "ANIM" // table for removed frames static CStaticArray aiRemFrameTable; // precalculated angles for rotations static CStaticArray aangAngles; // if rotations are compresed does loader also fills array of uncompresed rotations static BOOL bAllRotations = FALSE; void RememberUnCompresedRotatations(BOOL bRemember) { bAllRotations = bRemember; } CAnimSet::CAnimSet() { } CAnimSet::~CAnimSet() { } // conpres normal static void CompressAxis(const FLOAT3D &vNormal, UWORD &ubH, UWORD &ubP) { ANGLE h, p; const FLOAT &x = vNormal(1); const FLOAT &y = vNormal(2); const FLOAT &z = vNormal(3); // calculate pitch p = ASin(y); // if y is near +1 or -1 if (y>0.99999 || y<-0.99999) { // heading is irrelevant h = 0; // otherwise } else { // calculate heading h = ATan2(-x, -z); } h = (h/360.0f)+0.5f; p = (p/360.0f)+0.5f; ASSERT(h>=0 && h<=1); ASSERT(p>=0 && p<=1); ubH = UWORD(h*65535); ubP = UWORD(p*65535); } // try to remove 2. keyframe in rotation BOOL RemoveRotFrame(AnimRot &ar1,AnimRot &ar2,AnimRot &ar3,FLOAT fTreshold) { ANGLE3D ang1,ang2,ang2i,ang3; FLOATmatrix3D m2i; // calculate slerp factor for ar2' FLOAT fSlerpFactor = (FLOAT)(ar2.ar_iFrameNum - ar1.ar_iFrameNum)/(FLOAT)(ar3.ar_iFrameNum - ar1.ar_iFrameNum); // calculate ar2' FLOATquat3D q2i = Slerp(fSlerpFactor,ar1.ar_qRot,ar3.ar_qRot); // read precalculated values ang1 = aangAngles[ar1.ar_iFrameNum]; ang2 = aangAngles[ar2.ar_iFrameNum]; ang3 = aangAngles[ar3.ar_iFrameNum]; q2i.ToMatrix(m2i); DecomposeRotationMatrixNoSnap(ang2i,m2i); for(INDEX i=1;i<4;i++) { if( ((ang2(i) < ang3(i)) && (ang2(i) < ang1(i))) || ((ang2(i) > ang3(i)) && (ang2(i) > ang1(i))) ) { // this is extrem if(Abs(ang2(i)) > 0.1f) return FALSE; } FLOAT fErr = Abs(ang2(i)-ang2i(i)) / Abs(ang3(i) - ang1(i)); if(Abs(ang2(i)-ang2i(i)) < 0.1f) continue; if(fErr>fTreshold) return FALSE; } return TRUE; } // try to remove 2. keyrame in translation BOOL RemovePosFrame(AnimPos &ap1,AnimPos &ap2,AnimPos &ap3,FLOAT fTreshold) { FLOAT fLerpFactor = (FLOAT)(ap2.ap_iFrameNum - ap1.ap_iFrameNum)/(FLOAT)(ap3.ap_iFrameNum - ap1.ap_iFrameNum); FLOAT3D v2i = Lerp(ap1.ap_vPos,ap3.ap_vPos,fLerpFactor); FLOAT3D v1 = ap1.ap_vPos; FLOAT3D v2 = ap2.ap_vPos; FLOAT3D v3 = ap3.ap_vPos; for(INDEX i=1;i<4;i++) { if( ((v2(i) < v3(i)) && (v2(i) < v1(i))) || ((v2(i) > v3(i)) && (v2(i) > v1(i))) ) { // extrem if(Abs(v2(i)) > 0.001f) return FALSE; } FLOAT fErr = Abs(v2(i)-v2i(i)) / Abs(v3(i) - v1(i)); if(Abs(v2(i)-v2i(i)) < 0.001f) continue; if(fErr>fTreshold) return FALSE; } return TRUE; } // find next keyframe that havent been marked as removed INDEX FindNextFrame(INDEX ifnToFind) { INDEX ctfn = aiRemFrameTable.Count(); if(ifnToFind >= ctfn) return -1; if(aiRemFrameTable[ifnToFind] == FALSE) return ifnToFind; for(INDEX ifn=ifnToFind;ifn arRot; // for each removed frame for(INDEX ifnr=0;ifnr apPos; // count removed frames for(INDEX ifr=0;ifr aMorphs; INDEX ctme = an.an_ameMorphs.Count(); for(INDEX ime=0;ime=ctwm) break; FLOAT &fMorphFactor = me.me_aFactors[iwm]; // check if morph factor is 0 bMorphIsZero = fMorphFactor == 0; iwm++; } // dont remove this morph envelope if(!bMorphIsZero) { // copy this morphmap to temp array of morph envelopes MorphEnvelope &meNew = aMorphs.Push(); meNew = me; } } INDEX ctmeNew = aMorphs.Count(); // crate new array for morph envelopes an.an_ameMorphs.Clear(); an.an_ameMorphs.New(ctmeNew); // copy morph back to animations array of morph envelopes for(INDEX imeNew=0;imeNew0); ASSERT(pan!=NULL); // copy all animations to temp array CStaticArray animsTemp; animsTemp.New(ctan-1); INDEX ianNew=0; for(INDEX ian=0;ianWriteID_t(CChunkID(ANIMSET_ID)); // write version (*ostrFile)<<(INDEX)ANIMSET_VERSION; INDEX ctan = as_Anims.Count(); (*ostrFile)<Write_t(&be.be_mDefaultPos[0],sizeof(FLOAT)*12); // count positions INDEX ctp = be.be_apPos.Count(); // write position count (*ostrFile)<Write_t(&be.be_apPos[ip],sizeof(AnimPos)); } // count rotations INDEX ctRotations = be.be_arRot.Count(); (*ostrFile)<Write_t(&arRot,sizeof(AnimRot)); } INDEX ctOptRotations = be.be_arRotOpt.Count(); if(ctOptRotations>0) { // OPTIMISED ROTATIONS ARE NOT SAVED !!! // use RememberUnCompresedRotatations(); ASSERT(ctRotations>=ctOptRotations); } // write offsetlen (*ostrFile)<Write_t(&me.me_aFactors[0],sizeof(FLOAT)*ctmf); } } } // read from stream void CAnimSet::Read_t(CTStream *istrFile) { INDEX iFileVersion; // read chunk id istrFile->ExpectID_t(CChunkID(ANIMSET_ID)); // check file version (*istrFile)>>iFileVersion; if(iFileVersion != ANIMSET_VERSION) { ThrowF_t(TRANS("File '%s'.\nInvalid animset file version. Expected Ver \"%d\" but found \"%d\"\n"), (const char*)istrFile->GetDescription(),ANIMSET_VERSION,iFileVersion); } INDEX ctan; // read anims count (*istrFile)>>ctan; // create anims array as_Anims.New(ctan); for(int ian=0;ian>an.an_fnSourceFile; // read Anim ID (*istrFile>>pstrNameID); an.an_iID = ska_GetIDFromStringTable(pstrNameID); // read secperframe (*istrFile)>>an.an_fSecPerFrame; // read num of frames (*istrFile)>>an.an_iFrames; // read treshold (*istrFile)>>an.an_fTreshold; // read if compresion is used (*istrFile)>>an.an_bCompresed; // read bool if animstion uses custom speed (*istrFile)>>an.an_bCustomSpeed; INDEX ctbe; INDEX ctme; // read bone envelopes count (*istrFile)>>ctbe; // create bone envelopes array an.an_abeBones.New(ctbe); // read bone envelopes for(int ibe=0;ibe>pstrNameID; // read bone envelope ID be.be_iBoneID = ska_GetIDFromStringTable(pstrNameID); // read default pos(matrix12) istrFile->Read_t(&be.be_mDefaultPos[0],sizeof(FLOAT)*12); INDEX ctp; // read pos array (*istrFile)>>ctp; be.be_apPos.New(ctp); for(INDEX ip=0;ipRead_t(&be.be_apPos[ip],sizeof(AnimPos)); } INDEX ctr; // read rot array count (*istrFile)>>ctr; if(!an.an_bCompresed) { // create array for uncompresed rotations be.be_arRot.New(ctr); } else { // if flag is set to remember uncompresed rotations if(bAllRotations) { // create array for uncompresed rotations be.be_arRot.New(ctr); } // create array for compresed rotations be.be_arRotOpt.New(ctr); } for(INDEX ir=0;irRead_t(&arRot,sizeof(AnimRot)); if(!an.an_bCompresed) { be.be_arRot[ir] = arRot; } else { if(bAllRotations) { // fill uncompresed rotations be.be_arRot[ir] = arRot; } // optimize quaternions FLOAT3D vAxis; ANGLE aAngle; UWORD ubH,ubP; FLOATquat3D &qRot = arRot.ar_qRot; AnimRotOpt &aroRot = be.be_arRotOpt[ir]; qRot.ToAxisAngle(vAxis,aAngle); CompressAxis(vAxis,ubH,ubP); // compress angle aroRot.aro_aAngle = aAngle * ANG_COMPRESIONMUL; aroRot.aro_iFrameNum = arRot.ar_iFrameNum; aroRot.aro_ubH = ubH; aroRot.aro_ubP = ubP; be.be_arRotOpt[ir] = aroRot; } } // read offsetlen (*istrFile)>>be.be_OffSetLen; } // read morph envelopes (*istrFile)>>ctme; // create morph envelopes array an.an_ameMorphs.New(ctme); // read morph envelopes for(int ime=0;ime>pstrNameID; me.me_iMorphMapID = ska_GetIDFromStringTable(pstrNameID); INDEX ctmf; // read morph factors count (*istrFile)>>ctmf; // create morph factors array me.me_aFactors.New(ctmf); // read morph factors istrFile->Read_t(&me.me_aFactors[0],sizeof(FLOAT)*ctmf); } } } // clear animset void CAnimSet::Clear(void) { INDEX ctAnims = as_Anims.Count(); for(INDEX iAnims=0;iAnims