123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641 |
- /* Copyright (c) 2002-2012 Croteam Ltd.
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as published by
- the Free Software Foundation
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
- #include "StdH.h"
- #include <Engine/Ska/AnimSet.h>
- #include <Engine/Templates/StaticArray.h>
- #include <Engine/Base/CTString.h>
- #include <Engine/Ska/StringTable.h>
- #include <Engine/Templates/StaticArray.cpp>
- #include <Engine/templates/DynamicContainer.cpp>
- #include <Engine/Base/Stream.h>
- #include <Engine/Base/Console.h>
- #include <Engine/Math/Functions.h>
- #include <Engine/Math/Geometry.h>
- #include <Engine/Base/Timer.h>
- #define ANIMSET_VERSION 14
- #define ANIMSET_ID "ANIM"
- // table for removed frames
- static CStaticArray<BOOL> aiRemFrameTable;
- // precalculated angles for rotations
- static CStaticArray<ANGLE3D> 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<FLOAT>(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<ctfn;ifn++)
- {
- if(aiRemFrameTable[ifn] == FALSE) return ifn;
- }
- return -1;
- }
- // optimize all animations
- void CAnimSet::Optimize()
- {
- INDEX ctan=as_Anims.Count();
- for(INDEX ian=0;ian<ctan;ian++)
- {
- Animation &an = as_Anims[ian];
- //CalculateExtraSpins(an);
- OptimizeAnimation(an,an.an_fTreshold);
- }
- }
- // optimize animation
- void CAnimSet::OptimizeAnimation(Animation &an, FLOAT fTreshold)
- {
- INDEX ctfn = an.an_iFrames;
- INDEX ctbe = an.an_abeBones.Count();
- aiRemFrameTable.Clear();
- aiRemFrameTable.New(ctfn);
- aangAngles.Clear();
- aangAngles.New(ctfn);
- for(INDEX ibe=0;ibe<ctbe;ibe++)
- {
- BoneEnvelope &be = an.an_abeBones[ibe];
- // calculate length on bone in default pos
- be.be_OffSetLen = (FLOAT3D(be.be_mDefaultPos[3],be.be_mDefaultPos[7],be.be_mDefaultPos[11])).Length();
- // create a table for removed frames
- memset(&aiRemFrameTable[0],0,sizeof(BOOL)*ctfn);
- memset(&aangAngles[0],0,sizeof(ANGLE3D)*ctfn);
- // fill array of decomposed matrices
- FLOATmatrix3D mat;
- for(INDEX im=0;im<ctfn;im++)
- {
- be.be_arRot[im].ar_qRot.ToMatrix(mat);
- DecomposeRotationMatrixNoSnap(aangAngles[im],mat);
- }
- // try to remove rotations, steping by 2
- INDEX iloop=0;
- for(;iloop<ctfn;iloop++)
- {
- INDEX ctRemoved=0;
- // for each frame in bone envelope
- for(INDEX ifn=0;ifn<ctfn;ifn+=2)
- {
- INDEX iInd1 = FindNextFrame(ifn);
- INDEX iInd2 = FindNextFrame(iInd1+1);
- INDEX iInd3 = FindNextFrame(iInd2+1);
- // !!!! try only ind3
- if((iInd1 < 0)||(iInd2 < 0)||(iInd3 < 0)) break;
- AnimRot *parCurent = &be.be_arRot[iInd1];
- AnimRot *parNext = &be.be_arRot[iInd2];
- AnimRot *parLast = &be.be_arRot[iInd3];
- if(RemoveRotFrame(*parCurent,*parNext,*parLast,fTreshold))
- {
- aiRemFrameTable[iInd2] = TRUE;
- ctRemoved++;
- }
- }
- if(ctRemoved==0)
- {
- // exit if no keyframe has been removed
- break;
- }
- }
- // create temp array for rotations that are not removed
- CStaticStackArray<struct AnimRot> arRot;
- // for each removed frame
- for(INDEX ifnr=0;ifnr<ctfn;ifnr++)
- {
- // if frame is not in table for removed frames add it to temp arRot array
- if(!aiRemFrameTable[ifnr])
- {
- AnimRot &ar = arRot.Push();
- ar = be.be_arRot[ifnr];
- }
- }
- // count frames that are left
- INDEX ctfl = arRot.Count();
- // create new array for bone envelope
- be.be_arRot.Clear();
- be.be_arRot.New(ctfl);
- // copy array of rotaions
- INDEX fl=0;
- for(;fl<ctfl;fl++)
- {
- be.be_arRot[fl] = arRot[fl];
- }
- arRot.Clear();
-
- // do same thing for positions
- // clear table for removed frames
- memset(&aiRemFrameTable[0],0,sizeof(BOOL)*ctfn);
- // try to remove translations steping by 2
- for(iloop=0;iloop<ctfn;iloop++)
- {
- INDEX ctRemoved=0;
- for(INDEX ifn=0;ifn<ctfn;ifn+=2)
- {
- INDEX iInd1 = FindNextFrame(ifn);
- INDEX iInd2 = FindNextFrame(iInd1+1);
- INDEX iInd3 = FindNextFrame(iInd2+1);
- // !!!! try only ind3
- if((iInd1 < 0)||(iInd2 < 0)||(iInd3 < 0)) break;
- AnimPos *papCurent = &be.be_apPos[iInd1];
- AnimPos *papNext = &be.be_apPos[iInd2];
- AnimPos *papLast = &be.be_apPos[iInd3];
- if(RemovePosFrame(*papCurent,*papNext,*papLast,fTreshold))
- {
- aiRemFrameTable[iInd2] = TRUE;
- ctRemoved++;
- }
- }
- if(ctRemoved==0)
- {
- // exit if no keyframe has been removed
- break;
- }
- }
- CStaticStackArray<struct AnimPos> apPos;
- // count removed frames
- for(INDEX ifr=0;ifr<ctfn;ifr++)
- {
- if(!aiRemFrameTable[ifr])
- {
- AnimPos &ap = apPos.Push();
- ap = be.be_apPos[ifr];
- }
- }
- // count frames that are left
- ctfl = apPos.Count();
- // create new array for bone envelope
- be.be_apPos.Clear();
- be.be_apPos.New(ctfl);
- // copy array of translations
- for(fl=0;fl<ctfl;fl++)
- {
- be.be_apPos[fl] = apPos[fl];
- }
- apPos.Clear();
- }
- aiRemFrameTable.Clear();
- // if morph envelope has all factors 0 remove it
- CStaticStackArray<struct MorphEnvelope> aMorphs;
- INDEX ctme = an.an_ameMorphs.Count();
- for(INDEX ime=0;ime<ctme;ime++)
- {
- MorphEnvelope &me = an.an_ameMorphs[ime];
- // morph factors count
- INDEX ctwm=me.me_aFactors.Count();
- // index of wertex morph
- INDEX iwm=0;
- BOOL bMorphIsZero = TRUE;
- while(bMorphIsZero)
- {
- if(iwm>=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;imeNew<ctmeNew;imeNew++)
- {
- an.an_ameMorphs[imeNew] = aMorphs[imeNew];
- }
- }
- // add animation to animset
- void CAnimSet::AddAnimation(Animation *pan)
- {
- INDEX ctan = as_Anims.Count();
- as_Anims.Expand(ctan+1);
- Animation &an = as_Anims[ctan];
- an = *pan;
- }
- // remove animation from animset
- void CAnimSet::RemoveAnimation(Animation *pan)
- {
- INDEX ctan = as_Anims.Count();
- ASSERT(ctan>0);
- ASSERT(pan!=NULL);
-
- // copy all animations to temp array
- CStaticArray<struct Animation> animsTemp;
- animsTemp.New(ctan-1);
- INDEX ianNew=0;
- for(INDEX ian=0;ian<ctan;ian++)
- {
- Animation *panTemp = &as_Anims[ian];
- if(panTemp != pan)
- {
- // copy anims
- animsTemp[ianNew] = *panTemp;
- ianNew++;
- }
- }
- as_Anims = animsTemp;
- }
- // write to stream
- void CAnimSet::Write_t(CTStream *ostrFile)
- {
- // write id
- ostrFile->WriteID_t(CChunkID(ANIMSET_ID));
- // write version
- (*ostrFile)<<(INDEX)ANIMSET_VERSION;
- INDEX ctan = as_Anims.Count();
- (*ostrFile)<<ctan;
- for(int ian=0;ian<ctan;ian++)
- {
- Animation &an = as_Anims[ian];
- CTString pstrNameID = ska_GetStringFromTable(an.an_iID);
- // write anim source file
- (*ostrFile)<<an.an_fnSourceFile;
- // write anim id
- (*ostrFile)<<pstrNameID;
- // write secperframe
- (*ostrFile)<<an.an_fSecPerFrame;
- // write num of frames
- (*ostrFile)<<an.an_iFrames;
- // write treshold
- (*ostrFile)<<an.an_fTreshold;
- // write if compresion is used
- (*ostrFile)<<an.an_bCompresed;
- // write bool if animstion uses custom speed
- (*ostrFile)<<an.an_bCustomSpeed;
-
- INDEX ctbe = an.an_abeBones.Count();
- INDEX ctme = an.an_ameMorphs.Count();
- // write bone envelopes count
- (*ostrFile)<<ctbe;
- // for each bone envelope
- for(int ibe=0;ibe<ctbe;ibe++)
- {
- BoneEnvelope &be = an.an_abeBones[ibe];
- CTString pstrNameID = ska_GetStringFromTable(be.be_iBoneID);
- // write bone envelope ID
- (*ostrFile)<<pstrNameID;
- // write default pos(matrix12)
- ostrFile->Write_t(&be.be_mDefaultPos[0],sizeof(FLOAT)*12);
- // count positions
- INDEX ctp = be.be_apPos.Count();
- // write position count
- (*ostrFile)<<ctp;
- // for each position
- for(INDEX ip=0;ip<ctp;ip++)
- {
- // write position
- ostrFile->Write_t(&be.be_apPos[ip],sizeof(AnimPos));
- }
- // count rotations
- INDEX ctRotations = be.be_arRot.Count();
- (*ostrFile)<<ctRotations;
- // for each rotation
- for(INDEX ir=0;ir<ctRotations;ir++)
- {
- // write rotation
- AnimRot &arRot = be.be_arRot[ir];
- 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)<<be.be_OffSetLen;
- }
- // write morph envelopes
- (*ostrFile)<<ctme;
- for(int ime=0;ime<ctme;ime++)
- {
- MorphEnvelope &me = an.an_ameMorphs[ime];
- CTString pstrNameID = ska_GetStringFromTable(me.me_iMorphMapID);
- // write morph map ID
- (*ostrFile)<<pstrNameID;
- // write morph factors count
- INDEX ctmf = me.me_aFactors.Count();
- (*ostrFile)<<ctmf;
- 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<ctan;ian++)
- {
- Animation &an = as_Anims[ian];
- CTString pstrNameID;
- // read anim source file
- (*istrFile)>>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<ctbe;ibe++)
- {
- BoneEnvelope &be = an.an_abeBones[ibe];
- CTString pstrNameID;
- (*istrFile)>>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;ip<ctp;ip++)
- {
- istrFile->Read_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;ir<ctr;ir++)
- {
- AnimRot arRot;// = be.be_arRot[ir];
- istrFile->Read_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<ctme;ime++)
- {
- MorphEnvelope &me = an.an_ameMorphs[ime];
- CTString pstrNameID;
- // read morph envelope ID
- (*istrFile)>>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<ctAnims;iAnims++)
- {
- Animation &an = as_Anims[iAnims];
- INDEX ctBoneEnv = an.an_abeBones.Count();
- INDEX ctMorphEnv = an.an_ameMorphs.Count();
- for(INDEX iBoneEnv=0;iBoneEnv<ctBoneEnv;iBoneEnv++)
- {
- BoneEnvelope &be = an.an_abeBones[iBoneEnv];
- //be.be_aqvPlacement.Clear();
- be.be_apPos.Clear();
- be.be_arRot.Clear();
- }
- for(INDEX iMorphEnv=0;iMorphEnv<ctMorphEnv;iMorphEnv++)
- {
- MorphEnvelope &me = an.an_ameMorphs[iMorphEnv];
- me.me_aFactors.Clear();
- }
- an.an_abeBones.Clear();
- an.an_ameMorphs.Clear();
- }
- as_Anims.Clear();
- }
- // Count used memory
- SLONG CAnimSet::GetUsedMemory(void)
- {
- SLONG slMemoryUsed = sizeof(*this);
- INDEX ctAnims = as_Anims.Count();
- for(INDEX ias=0;ias<ctAnims;ias++) {
- Animation &an = as_Anims[ias];
- slMemoryUsed+=sizeof(an);
- // for each bone envelope
- INDEX ctbe = an.an_abeBones.Count();
- for(INDEX ibe=0;ibe<ctbe;ibe++) {
- BoneEnvelope &be = an.an_abeBones[ibe];
- slMemoryUsed+=sizeof(be);
- slMemoryUsed+=be.be_apPos.Count() * sizeof(AnimPos);
- slMemoryUsed+=be.be_arRot.Count() * sizeof(AnimRot);
- slMemoryUsed+=be.be_arRotOpt.Count() * sizeof(AnimRotOpt);
- }
- // for each morph envelope
- INDEX ctme = an.an_ameMorphs.Count();
- for(INDEX ime=0;ime<ctme;ime++) {
- MorphEnvelope &me = an.an_ameMorphs[ime];
- slMemoryUsed+=sizeof(me);
- slMemoryUsed+=sizeof(FLOAT) * me.me_aFactors.Count() + 12;
- }
- }
- return slMemoryUsed;
- }
|