// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include "nel/misc/quat.h" #include "nel/misc/common.h" #include "nel/misc/algo.h" #include "nel/3d/track_sampled_common.h" #include "nel/misc/vectord.h" using namespace NLMISC; using namespace std; namespace NL3D { // *************************************************************************** // *************************************************************************** // CTrackSampledCommon // *************************************************************************** // *************************************************************************** // *************************************************************************** CTrackSampledCommon::CTrackSampledCommon() { _LoopMode= true; } // *************************************************************************** CTrackSampledCommon::~CTrackSampledCommon() { } // *************************************************************************** bool CTrackSampledCommon::getLoopMode() const { return _LoopMode; } // *************************************************************************** TAnimationTime CTrackSampledCommon::getBeginTime () const { return _BeginTime; } // *************************************************************************** TAnimationTime CTrackSampledCommon::getEndTime () const { return _EndTime; } // *************************************************************************** void CTrackSampledCommon::CTimeBlock::serial(NLMISC::IStream &f) { (void)f.serialVersion(0); f.serial(TimeOffset); f.serial(KeyOffset); f.serial(Times); } // *************************************************************************** void CTrackSampledCommon::serialCommon(NLMISC::IStream &f) { (void)f.serialVersion(0); f.serial(_LoopMode); f.serial(_BeginTime); f.serial(_EndTime) ; f.serial(_TotalRange); f.serial(_OOTotalRange); f.serial(_DeltaTime); f.serial(_OODeltaTime); f.serial(_TimeBlocks); } // *************************************************************************** void CTrackSampledCommon::buildCommon(const std::vector &timeList, float beginTime, float endTime) { nlassert( endTime>beginTime || (beginTime==endTime && timeList.size()<=1) ); uint i; // reset. uint numKeys= timeList.size(); _TimeBlocks.clear(); // Special case of 0 or 1 key. //=================== if(numKeys<=1) { _BeginTime= beginTime; _EndTime= endTime; _TotalRange= 0; _OOTotalRange= 0; _DeltaTime= 0; _OODeltaTime= 0; if(numKeys==1) { _TimeBlocks.resize(1); _TimeBlocks[0].TimeOffset= 0; _TimeBlocks[0].Times.resize(1); _TimeBlocks[0].Times[0]= 0; } return; } // Compute All Time blocks. //=================== sint32 lastBlockFrame= -1000000; nlassert(timeList[0] == 0); // Header info for creating timeBlocks vector timeBlockKeyId; vector timeBlockNumKeys; // compute how many time block we need. for(i=0; i0) { nlassert(timeList[i]>timeList[i-1]); nlassert(timeList[i]-timeList[i-1] <= 255 ); } // If the current frame is to far from the last TimeBlock frame (or if 1st timeBlock), must create a new timeBlock if(timeList[i]-lastBlockFrame>255) { // create a new timeblock timeBlockKeyId.push_back(i); // Add this key to this new time Block (numKey == 1). timeBlockNumKeys.push_back(1); lastBlockFrame= timeList[i]; } else { // Add this key to the timeBlock. timeBlockNumKeys[timeBlockNumKeys.size()-1]++; } } // Build the timeBlocks. _TimeBlocks.resize(timeBlockKeyId.size()); for(i=0; i0); _DeltaTime= (_EndTime-_BeginTime) / totalFrameCount; _OODeltaTime= (float)(1.0 / _DeltaTime); // Compute range of anim _TotalRange= _EndTime-_BeginTime; _OOTotalRange= float(1.0/_TotalRange); } // *************************************************************************** void CTrackSampledCommon::setLoopMode(bool mode) { _LoopMode= mode; } // *************************************************************************** CTrackSampledCommon::TEvalType CTrackSampledCommon::evalTime (const TAnimationTime& date, uint numKeys, uint &keyId0, uint &keyId1, float &interpValue) { /* IF YOU CHANGE THIS CODE, CHANGE too CTrackSampledQuatSmallHeader */ // Empty? quit if(numKeys==0) return EvalDiscard; // One Key? easy, and quit. if(numKeys==1) { keyId0= 0; return EvalKey0; } // manage Loop //===================== float localTime; if(_LoopMode) { nlassert(_TotalRange>0); // get relative to BeginTime. localTime= date-_BeginTime; // force us to be in interval [0, _TotalRange[. if( localTime<0 || localTime>=_TotalRange ) { double d= localTime*_OOTotalRange; // floor(d) is the truncated number of loops. d= localTime- floor(d)*_TotalRange; localTime= (float)d; // For precision problems, ensure correct range. if(localTime<0 || localTime >= _TotalRange) localTime= 0; } } else { // get relative to BeginTime. localTime= date-_BeginTime; } // Find the first key before localTime //===================== // get the frame in the track. sint frame= (sint)floor(localTime*_OODeltaTime); // clamp to uint16 clamp(frame, 0, 65535); // Search the TimeBlock. CTimeBlock keyTB; keyTB.TimeOffset= frame; uint tbId; tbId= searchLowerBound(_TimeBlocks.getPtr(), _TimeBlocks.size(), keyTB); // get this timeBlock. CTimeBlock &timeBlock= _TimeBlocks[tbId]; // get frame relative to this timeBlock. sint frameRel= frame-timeBlock.TimeOffset; // clamp to uint8 clamp(frameRel, 0, 255); // get the key in this timeBlock. uint keyIdRel; keyIdRel= searchLowerBound(timeBlock.Times.getPtr(), timeBlock.Times.size(), (uint8)frameRel); // Get the Frame and Value of Key0. uint frameKey0= timeBlock.TimeOffset + timeBlock.Times[keyIdRel]; // this is the key to evaluate keyId0= timeBlock.KeyOffset + keyIdRel; // Interpolate with next key //===================== // If not the last Key if(keyId0= timeBlock.Times.size() ) { nlassert(tbId+1<_TimeBlocks.size()); frameKey1= _TimeBlocks[tbId+1].TimeOffset; } else { frameKey1= timeBlock.TimeOffset + timeBlock.Times[keyIdRel+1]; } // unpack time. float time0= frameKey0*_DeltaTime; float time1= frameKey1*_DeltaTime; // interpolate. float t= (localTime-time0); // If difference is one frame, optimize. if(frameKey1-frameKey0==1) t*= _OODeltaTime; else t/= (time1-time0); clamp(t, 0.f, 1.f); // store this interp value. interpValue= t; return EvalInterpolate; } // else (last key of anim), just eval this key. return EvalKey0; } // *************************************************************************** void CTrackSampledCommon::applySampleDivisorCommon(uint sampleDivisor, std::vector &keepKeys) { nlassert(sampleDivisor>=2); uint i,j; /* NB: to be faster, if we have multiple timeblock (rare, cause implies the anim>8.5 sec), the number of time block is kept after this process, either if it could be lowered. NB: for same reason, the first and last key of each timeBlock is kept to be simpler, and to avoid bug in searchLowerBound() (we must keep first key of each timeBlock) */ // clear keepKeys.clear(); // **** build the key indices to keep static std::vector blockKeepStart; static std::vector blockKeepEnd; blockKeepStart.resize(_TimeBlocks.size()); blockKeepEnd.resize(_TimeBlocks.size()); // must Keep the first and last key. uint lastKeyTime= 0; for(i=0;i<_TimeBlocks.size();i++) { CTimeBlock &timeBlock= _TimeBlocks[i]; // keep track of the start new key for this block blockKeepStart[i]= keepKeys.size(); for(j=0;j= than the sampleDivisor, add it! if( (keyTime - lastKeyTime >= sampleDivisor) || // add it also if it is the first or last key of the block (j==0 || j==timeBlock.Times.size()-1) ) { lastKeyTime= keyTime; keepKeys.push_back(j+timeBlock.KeyOffset); } } // keep track of the end (not included) new key for this block blockKeepEnd[i]= keepKeys.size(); } // **** rebuild the TimeBlocks for(i=0;i<_TimeBlocks.size();i++) { CTimeBlock &timeBlock= _TimeBlocks[i]; uint keepStart= blockKeepStart[i]; uint keepEnd= blockKeepEnd[i]; NLMISC::CObjectVector newKeys; newKeys.resize(keepEnd-keepStart); for(uint j=0;j