2016-03-12 01:20:51 +01:00
|
|
|
/* 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. */
|
2016-03-11 14:57:17 +01:00
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
#include "Engine/StdH.h"
|
2016-03-11 14:57:17 +01:00
|
|
|
#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>
|
2016-03-29 03:03:54 +02:00
|
|
|
#include <Engine/Templates/DynamicContainer.cpp>
|
2016-03-11 14:57:17 +01:00
|
|
|
#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'
|
2016-03-29 03:03:54 +02:00
|
|
|
FLOATquat3D q2i = Slerp(fSlerpFactor,ar1.ar_qRot,ar3.ar_qRot);
|
2016-03-11 14:57:17 +01:00
|
|
|
// 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);
|
|
|
|
}
|
2016-03-29 03:03:54 +02:00
|
|
|
// try to remove rotations, stepping by 2
|
|
|
|
INDEX iloop;
|
|
|
|
for(iloop=0;iloop<ctfn;iloop++)
|
2016-03-11 14:57:17 +01:00
|
|
|
{
|
|
|
|
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
|
2016-03-29 03:03:54 +02:00
|
|
|
INDEX fl;
|
|
|
|
for(fl=0;fl<ctfl;fl++)
|
2016-03-11 14:57:17 +01:00
|
|
|
{
|
|
|
|
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);
|
2016-03-29 03:03:54 +02:00
|
|
|
// try to remove translations stepping by 2
|
2016-03-11 14:57:17 +01:00
|
|
|
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;
|
2016-03-29 03:03:54 +02:00
|
|
|
// write bool if animation uses custom speed
|
2016-03-11 14:57:17 +01:00
|
|
|
(*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;
|
2016-03-29 03:03:54 +02:00
|
|
|
// read bool if animation uses custom speed
|
2016-03-11 14:57:17 +01:00
|
|
|
(*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)
|
2016-03-29 03:03:54 +02:00
|
|
|
for (int i = 0; i < 12; i++)
|
|
|
|
(*istrFile) >> be.be_mDefaultPos[i];
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
INDEX ctp;
|
|
|
|
// read pos array
|
|
|
|
(*istrFile)>>ctp;
|
|
|
|
be.be_apPos.New(ctp);
|
|
|
|
for(INDEX ip=0;ip<ctp;ip++)
|
|
|
|
{
|
2016-03-29 03:03:54 +02:00
|
|
|
(*istrFile)>>be.be_apPos[ip];
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
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];
|
2016-03-29 03:03:54 +02:00
|
|
|
(*istrFile) >> arRot;
|
2016-03-11 14:57:17 +01:00
|
|
|
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
|
2016-03-29 03:03:54 +02:00
|
|
|
for (INDEX i = 0; i < ctmf; i++)
|
|
|
|
(*istrFile) >> me.me_aFactors[i];
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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;
|
|
|
|
}
|