00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "std3d.h"
00027
00028 #include "3d/animation_optimizer.h"
00029 #include "nel/misc/mem_stream.h"
00030 #include "nel/misc/vectord.h"
00031 #include "3d/track.h"
00032 #include "3d/track_keyframer.h"
00033 #include "3d/animation.h"
00034 #include "3d/track_sampled_quat.h"
00035 #include "3d/track_sampled_vector.h"
00036
00037
00038 using namespace NLMISC;
00039 using namespace std;
00040
00041
00042 namespace NL3D
00043 {
00044
00045
00046
00047 CAnimationOptimizer::CAnimationOptimizer()
00048 {
00049 _SampleFrameRate= 30;
00050 _QuaternionThresholdLowPrec= 1.0 - 0.0001;
00051 _QuaternionThresholdHighPrec= 1.0 - 0.000001;
00052 _VectorThresholdLowPrec= 0.001;
00053 _VectorThresholdHighPrec= 0.0001;
00054 }
00055
00056
00057
00058 void CAnimationOptimizer::setQuaternionThreshold(double lowPrecThre, double highPrecThre)
00059 {
00060 nlassert(lowPrecThre>=0);
00061 nlassert(highPrecThre>=0);
00062 _QuaternionThresholdLowPrec= 1.0 - lowPrecThre;
00063 _QuaternionThresholdHighPrec= 1.0 - highPrecThre;
00064 }
00065
00066
00067
00068 void CAnimationOptimizer::setVectorThreshold(double lowPrecThre, double highPrecThre)
00069 {
00070 nlassert(lowPrecThre>=0);
00071 nlassert(highPrecThre>=0);
00072 _VectorThresholdLowPrec= lowPrecThre;
00073 _VectorThresholdHighPrec= highPrecThre;
00074 }
00075
00076
00077
00078 void CAnimationOptimizer::setSampleFrameRate(float frameRate)
00079 {
00080 nlassert(frameRate>0);
00081 _SampleFrameRate= frameRate;
00082 }
00083
00084
00085
00086 void CAnimationOptimizer::optimize(const CAnimation &animIn, CAnimation &animOut)
00087 {
00088
00089 contReset(animOut);
00090
00091
00092 set<string> setString;
00093 animIn.getTrackNames (setString);
00094 set<string>::iterator it;
00095
00096 for(it=setString.begin();it!=setString.end();it++)
00097 {
00098 const string &trackName= *it;
00099 uint trackId= animIn.getIdTrackByName(trackName);
00100 nlassert(trackId!=CAnimation::NotFound);
00101 const ITrack *track= animIn.getTrack(trackId);
00102
00103
00104 ITrack *newTrack;
00105 if(isTrackOptimisable(track))
00106 {
00107
00108 if( isLowPrecisionTrack(trackName) )
00109 {
00110 _QuaternionThreshold= _QuaternionThresholdLowPrec;
00111 _VectorThreshold= _VectorThresholdLowPrec;
00112 }
00113 else
00114 {
00115 _QuaternionThreshold= _QuaternionThresholdHighPrec;
00116 _VectorThreshold= _VectorThresholdHighPrec;
00117 }
00118
00119
00120 newTrack= optimizeTrack(track);
00121 }
00122 else
00123 {
00124
00125 newTrack= cloneTrack(track);
00126 }
00127
00128
00129 animOut.addTrack(trackName, newTrack);
00130 }
00131
00132
00133 animOut.setMinEndTime (animIn.getEndTime ());
00134 nlassert (animOut.getEndTime() == animIn.getEndTime());
00135 }
00136
00137
00138 ITrack *CAnimationOptimizer::cloneTrack(const ITrack *trackIn)
00139 {
00140 CMemStream memStream;
00141
00142
00143 ITrack *trackInSerial= const_cast<ITrack*>(trackIn);
00144 memStream.serialPolyPtr(trackInSerial);
00145
00146
00147 memStream.invert();
00148 ITrack *ret= NULL;
00149 memStream.serialPolyPtr(ret);
00150
00151 return ret;
00152 }
00153
00154
00155
00156 bool CAnimationOptimizer::isTrackOptimisable(const ITrack *trackIn)
00157 {
00158 nlassert(trackIn);
00159
00160
00161 if( dynamic_cast<const CTrackKeyFramerTCBQuat*>(trackIn) ||
00162 dynamic_cast<const CTrackKeyFramerBezierQuat*>(trackIn) ||
00163 dynamic_cast<const CTrackKeyFramerLinearQuat*>(trackIn) )
00164 return true;
00165
00166
00167 if( dynamic_cast<const CTrackKeyFramerTCBVector*>(trackIn) ||
00168 dynamic_cast<const CTrackKeyFramerBezierVector*>(trackIn) ||
00169 dynamic_cast<const CTrackKeyFramerLinearVector*>(trackIn) )
00170 return true;
00171
00172 return false;
00173 }
00174
00175
00176
00177 ITrack *CAnimationOptimizer::optimizeTrack(const ITrack *trackIn)
00178 {
00179
00180 float beginTime= trackIn->getBeginTime();
00181 float endTime= trackIn->getEndTime();
00182 nlassert(endTime>=beginTime);
00183
00184
00185 uint numSamples= (uint)ceil( (endTime-beginTime)*_SampleFrameRate);
00186 numSamples= max(1U, numSamples);
00187 nlassert(numSamples<65535);
00188
00189
00190
00191
00192 if( dynamic_cast<const CAnimatedValueQuat *>(&trackIn->getValue()) )
00193 {
00194
00195 sampleQuatTrack(trackIn, beginTime, endTime, numSamples);
00196
00197
00198 if( testConstantQuatTrack() )
00199 {
00200
00201 CTrackDefaultQuat *trackDefault= new CTrackDefaultQuat;
00202
00203 trackDefault->setValue(_QuatKeyList[0]);
00204
00205
00206 return trackDefault;
00207 }
00208
00209 else
00210 {
00211
00212 optimizeQuatTrack();
00213
00214
00215 CTrackSampledQuat *trackSQ= new CTrackSampledQuat;
00216
00217
00218 trackSQ->setLoopMode(trackIn->getLoopMode());
00219
00220
00221 trackSQ->build(_TimeList, _QuatKeyList, beginTime, endTime);
00222
00223
00224 return trackSQ;
00225 }
00226 }
00227
00228
00229 else if( dynamic_cast<const CAnimatedValueVector *>(&trackIn->getValue()) )
00230 {
00231
00232 sampleVectorTrack(trackIn, beginTime, endTime, numSamples);
00233
00234
00235 if( testConstantVectorTrack() )
00236 {
00237
00238 CTrackDefaultVector *trackDefault= new CTrackDefaultVector;
00239
00240 trackDefault->setValue(_VectorKeyList[0]);
00241
00242
00243 return trackDefault;
00244 }
00245
00246 else
00247 {
00248
00249 optimizeVectorTrack();
00250
00251
00252 CTrackSampledVector *trackSV= new CTrackSampledVector;
00253
00254
00255 trackSV->setLoopMode(trackIn->getLoopMode());
00256
00257
00258 trackSV->build(_TimeList, _VectorKeyList, beginTime, endTime);
00259
00260
00261 return trackSV;
00262 }
00263 }
00264 else
00265 {
00266
00267 nlstop;
00268
00269 return cloneTrack(trackIn);
00270 }
00271 }
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282 void CAnimationOptimizer::sampleQuatTrack(const ITrack *trackIn, float beginTime, float endTime, uint numSamples)
00283 {
00284
00285 _TimeList.resize(numSamples);
00286 _QuatKeyList.resize(numSamples);
00287
00288
00289 float t= beginTime;
00290 float dt= 0;
00291 if(numSamples>1)
00292 dt= (endTime-beginTime)/(numSamples-1);
00293 for(uint i=0;i<numSamples; i++, t+=dt)
00294 {
00295 CQuat quat;
00296
00297
00298 if(i==numSamples-1)
00299 t= endTime;
00300
00301
00302 const_cast<ITrack*>(trackIn)->interpolate(t, quat);
00303
00304
00305 quat.normalize();
00306
00307
00308 if(i>0)
00309 {
00310 quat.makeClosest(_QuatKeyList[i-1]);
00311 }
00312
00313
00314 _TimeList[i]= i;
00315 _QuatKeyList[i]= quat;
00316 }
00317
00318 }
00319
00320
00321 bool CAnimationOptimizer::testConstantQuatTrack()
00322 {
00323 uint numSamples= _QuatKeyList.size();
00324 nlassert(numSamples>0);
00325
00326
00327 CQuat quatRef= _QuatKeyList[0];
00328 for(uint i=0;i<numSamples;i++)
00329 {
00330
00331 if(!nearlySameQuaternion(quatRef, _QuatKeyList[i]))
00332 return false;
00333 }
00334
00335
00336 return true;
00337 }
00338
00339
00340
00341 void CAnimationOptimizer::optimizeQuatTrack()
00342 {
00343 uint numSamples= _QuatKeyList.size();
00344 nlassert(numSamples>0);
00345
00346
00347 if(numSamples<=2)
00348 return;
00349
00350
00351 std::vector<uint16> optTimeList;
00352 std::vector<CQuat> optKeyList;
00353 optTimeList.reserve(numSamples);
00354 optKeyList.reserve(numSamples);
00355
00356
00357 optTimeList.push_back(_TimeList[0]);
00358 optKeyList.push_back(_QuatKeyList[0]);
00359 double timeRef= _TimeList[0];
00360 CQuatD quatRef= _QuatKeyList[0];
00361
00362
00363 for(uint i=1; i<numSamples-1; i++)
00364 {
00365 CQuatD quatCur= _QuatKeyList[i];
00366 CQuatD quatNext= _QuatKeyList[i+1];
00367 double timeCur= _TimeList[i];
00368 double timeNext= _TimeList[i+1];
00369
00370
00371 bool mustAdd= false;
00372
00373
00374 if(timeNext-timeRef>255)
00375 {
00376 mustAdd= true;
00377 }
00378
00379 else if( CQuatD::dotProduct(quatCur, quatRef)<0 || CQuatD::dotProduct(quatNext, quatRef)<0 )
00380 {
00381 mustAdd= true;
00382 }
00383
00384 else
00385 {
00386
00387 if( nearlySameQuaternion(quatRef, quatCur) && nearlySameQuaternion(quatRef, quatNext) )
00388 mustAdd= false;
00389 else
00390 {
00391
00392 CQuatD quatInterpolated;
00393 double t= (timeCur-timeRef)/(timeNext/timeRef);
00394 quatInterpolated= CQuatD::slerp(quatRef, quatNext, (float)t);
00395
00396
00397 if( !nearlySameQuaternion(quatCur, quatInterpolated) )
00398 mustAdd= true;
00399 }
00400 }
00401
00402
00403 if(mustAdd)
00404 {
00405 optTimeList.push_back(_TimeList[i]);
00406 optKeyList.push_back(_QuatKeyList[i]);
00407 timeRef= _TimeList[i];
00408 quatRef= _QuatKeyList[i];
00409 }
00410 }
00411
00412
00413 optTimeList.push_back(_TimeList[numSamples-1]);
00414 optKeyList.push_back(_QuatKeyList[numSamples-1]);
00415
00416
00417 _TimeList= optTimeList;
00418 _QuatKeyList= optKeyList;
00419 }
00420
00421
00422
00423 bool CAnimationOptimizer::nearlySameQuaternion(const CQuatD &quat0, const CQuatD &quat1)
00424 {
00425
00426 if(quat0==quat1 || quat0==-quat1)
00427 return true;
00428
00429
00430 CQuatD quatDif;
00431 quatDif= quat1 * quat0.conjugate();
00432
00433 if(quatDif.w<0)
00434 quatDif= -quatDif;
00435
00436
00437 return (quatDif.w >= _QuaternionThreshold);
00438 }
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449 void CAnimationOptimizer::sampleVectorTrack(const ITrack *trackIn, float beginTime, float endTime, uint numSamples)
00450 {
00451
00452 _TimeList.resize(numSamples);
00453 _VectorKeyList.resize(numSamples);
00454
00455
00456 float t= beginTime;
00457 float dt= 0;
00458 if(numSamples>1)
00459 dt= (endTime-beginTime)/(numSamples-1);
00460 for(uint i=0;i<numSamples; i++, t+=dt)
00461 {
00462 CVector vector;
00463
00464
00465 if(i==numSamples-1)
00466 t= endTime;
00467
00468
00469 const_cast<ITrack*>(trackIn)->interpolate(t, vector);
00470
00471
00472 _TimeList[i]= i;
00473 _VectorKeyList[i]= vector;
00474 }
00475
00476 }
00477
00478
00479 bool CAnimationOptimizer::testConstantVectorTrack()
00480 {
00481 uint numSamples= _VectorKeyList.size();
00482 nlassert(numSamples>0);
00483
00484
00485 CVector vectorRef= _VectorKeyList[0];
00486 for(uint i=0;i<numSamples;i++)
00487 {
00488
00489 if(!nearlySameVector(vectorRef, _VectorKeyList[i]))
00490 return false;
00491 }
00492
00493
00494 return true;
00495 }
00496
00497
00498
00499 void CAnimationOptimizer::optimizeVectorTrack()
00500 {
00501 uint numSamples= _VectorKeyList.size();
00502 nlassert(numSamples>0);
00503
00504
00505 if(numSamples<=2)
00506 return;
00507
00508
00509 std::vector<uint16> optTimeList;
00510 std::vector<CVector> optKeyList;
00511 optTimeList.reserve(numSamples);
00512 optKeyList.reserve(numSamples);
00513
00514
00515 optTimeList.push_back(_TimeList[0]);
00516 optKeyList.push_back(_VectorKeyList[0]);
00517 double timeRef= _TimeList[0];
00518 CVectorD vectorRef= _VectorKeyList[0];
00519
00520
00521 for(uint i=1; i<numSamples-1; i++)
00522 {
00523 CVectorD vectorCur= _VectorKeyList[i];
00524 CVectorD vectorNext= _VectorKeyList[i+1];
00525 double timeCur= _TimeList[i];
00526 double timeNext= _TimeList[i+1];
00527
00528
00529 bool mustAdd= false;
00530
00531
00532 if(timeNext-timeRef>255)
00533 {
00534 mustAdd= true;
00535 }
00536
00537 else
00538 {
00539
00540 if( nearlySameVector(vectorRef, vectorCur) && nearlySameVector(vectorRef, vectorNext) )
00541 mustAdd= false;
00542 else
00543 {
00544
00545 CVectorD vectorInterpolated;
00546 double t= (timeCur-timeRef)/(timeNext/timeRef);
00547 vectorInterpolated= vectorRef*(1-t) + vectorNext*t;
00548
00549
00550 if( !nearlySameVector(vectorCur, vectorInterpolated) )
00551 mustAdd= true;
00552 }
00553 }
00554
00555
00556 if(mustAdd)
00557 {
00558 optTimeList.push_back(_TimeList[i]);
00559 optKeyList.push_back(_VectorKeyList[i]);
00560 timeRef= _TimeList[i];
00561 vectorRef= _VectorKeyList[i];
00562 }
00563 }
00564
00565
00566 optTimeList.push_back(_TimeList[numSamples-1]);
00567 optKeyList.push_back(_VectorKeyList[numSamples-1]);
00568
00569
00570 _TimeList= optTimeList;
00571 _VectorKeyList= optKeyList;
00572 }
00573
00574
00575
00576 bool CAnimationOptimizer::nearlySameVector(const CVectorD &v0, const CVectorD &v1)
00577 {
00578
00579 if(v0==v1)
00580 return true;
00581
00582
00583 CVectorD vDif;
00584 vDif= v1-v0;
00585
00586
00587 return (vDif.norm() <= _VectorThreshold);
00588 }
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599 void CAnimationOptimizer::addLowPrecisionTrack(const std::string &name)
00600 {
00601 _LowPrecTrackKeyName.push_back(name);
00602 }
00603
00604
00605 void CAnimationOptimizer::clearLowPrecisionTracks()
00606 {
00607 _LowPrecTrackKeyName.clear();
00608 }
00609
00610
00611 bool CAnimationOptimizer::isLowPrecisionTrack(const std::string &trackName)
00612 {
00613 for(uint i=0; i<_LowPrecTrackKeyName.size(); i++)
00614 {
00615
00616 if( trackName.find(_LowPrecTrackKeyName[i]) != string::npos )
00617 return true;
00618 }
00619
00620
00621 return false;
00622 }
00623
00624
00625 }