1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/misc/quat.h"
20 #include "nel/misc/common.h"
21 #include "nel/misc/algo.h"
22 #include "nel/3d/track_sampled_common.h"
23 #include "nel/misc/vectord.h"
25 using namespace NLMISC
;
36 // ***************************************************************************
37 // ***************************************************************************
38 // CTrackSampledCommon
39 // ***************************************************************************
40 // ***************************************************************************
43 // ***************************************************************************
44 CTrackSampledCommon::CTrackSampledCommon()
49 // ***************************************************************************
50 CTrackSampledCommon::~CTrackSampledCommon()
54 // ***************************************************************************
55 bool CTrackSampledCommon::getLoopMode() const
60 // ***************************************************************************
61 TAnimationTime
CTrackSampledCommon::getBeginTime () const
66 // ***************************************************************************
67 TAnimationTime
CTrackSampledCommon::getEndTime () const
73 // ***************************************************************************
74 void CTrackSampledCommon::CTimeBlock::serial(NLMISC::IStream
&f
)
76 (void)f
.serialVersion(0);
83 // ***************************************************************************
84 void CTrackSampledCommon::serialCommon(NLMISC::IStream
&f
)
86 (void)f
.serialVersion(0);
91 f
.serial(_TotalRange
);
92 f
.serial(_OOTotalRange
);
94 f
.serial(_OODeltaTime
);
95 f
.serial(_TimeBlocks
);
99 // ***************************************************************************
100 void CTrackSampledCommon::buildCommon(const std::vector
<uint16
> &timeList
, float beginTime
, float endTime
)
102 nlassert( endTime
>beginTime
|| (beginTime
==endTime
&& timeList
.size()<=1) );
106 uint numKeys
= (uint
)timeList
.size();
109 // Special case of 0 or 1 key.
110 //===================
113 _BeginTime
= beginTime
;
121 _TimeBlocks
.resize(1);
122 _TimeBlocks
[0].TimeOffset
= 0;
123 _TimeBlocks
[0].Times
.resize(1);
124 _TimeBlocks
[0].Times
[0]= 0;
130 // Compute All Time blocks.
131 //===================
132 sint32 lastBlockFrame
= -1000000;
133 nlassert(timeList
[0] == 0);
134 // Header info for creating timeBlocks
135 vector
<uint
> timeBlockKeyId
;
136 vector
<uint
> timeBlockNumKeys
;
138 // compute how many time block we need.
139 for(i
=0; i
<numKeys
; i
++)
141 // verify growing order, and time difference.
144 nlassert(timeList
[i
]>timeList
[i
-1]);
145 nlassert(timeList
[i
]-timeList
[i
-1] <= 255 );
147 // If the current frame is to far from the last TimeBlock frame (or if 1st timeBlock), must create a new timeBlock
148 if(timeList
[i
]-lastBlockFrame
>255)
150 // create a new timeblock
151 timeBlockKeyId
.push_back(i
);
152 // Add this key to this new time Block (numKey == 1).
153 timeBlockNumKeys
.push_back(1);
154 lastBlockFrame
= timeList
[i
];
158 // Add this key to the timeBlock.
159 timeBlockNumKeys
[timeBlockNumKeys
.size()-1]++;
163 // Build the timeBlocks.
164 _TimeBlocks
.resize((uint32
)timeBlockKeyId
.size());
165 for(i
=0; i
<timeBlockKeyId
.size(); i
++)
167 CTimeBlock
&timeBlock
= _TimeBlocks
[i
];
168 uint firstKeyId
= timeBlockKeyId
[i
];
169 uint numKeys
= timeBlockNumKeys
[i
];
170 // compute the offset time and key
171 timeBlock
.KeyOffset
= firstKeyId
;
172 timeBlock
.TimeOffset
= timeList
[firstKeyId
];
173 // create array of key
174 timeBlock
.Times
.resize(numKeys
);
175 for(uint j
=0;j
<timeBlock
.Times
.size(); j
++)
177 // get the key time and make it local to the timeBlock.
178 timeBlock
.Times
[j
]= timeList
[firstKeyId
+j
] - timeBlock
.TimeOffset
;
182 // Compute other params
183 //===================
184 _BeginTime
= beginTime
;
186 // compute deltatime for a frame to another
187 uint totalFrameCount
= timeList
[numKeys
-1] - timeList
[0];
188 nlassert(totalFrameCount
>0);
189 _DeltaTime
= (_EndTime
-_BeginTime
) / totalFrameCount
;
190 _OODeltaTime
= (float)(1.0 / _DeltaTime
);
191 // Compute range of anim
192 _TotalRange
= _EndTime
-_BeginTime
;
193 _OOTotalRange
= float(1.0/_TotalRange
);
197 // ***************************************************************************
198 void CTrackSampledCommon::setLoopMode(bool mode
)
204 // ***************************************************************************
205 CTrackSampledCommon::TEvalType
CTrackSampledCommon::evalTime (const TAnimationTime
& date
, uint numKeys
, uint
&keyId0
, uint
&keyId1
, float &interpValue
)
207 /* IF YOU CHANGE THIS CODE, CHANGE too CTrackSampledQuatSmallHeader
214 // One Key? easy, and quit.
222 //=====================
226 nlassert(_TotalRange
>0);
227 // get relative to BeginTime.
228 localTime
= date
-_BeginTime
;
230 // force us to be in interval [0, _TotalRange[.
231 if( localTime
<0 || localTime
>=_TotalRange
)
233 double d
= localTime
*_OOTotalRange
;
235 // floor(d) is the truncated number of loops.
236 d
= localTime
- floor(d
)*_TotalRange
;
239 // For precision problems, ensure correct range.
240 if(localTime
<0 || localTime
>= _TotalRange
)
247 // get relative to BeginTime.
248 localTime
= date
-_BeginTime
;
252 // Find the first key before localTime
253 //=====================
254 // get the frame in the track.
255 sint frame
= (sint
)floor(localTime
*_OODeltaTime
);
257 clamp(frame
, 0, 65535);
259 // Search the TimeBlock.
261 keyTB
.TimeOffset
= frame
;
263 tbId
= searchLowerBound(_TimeBlocks
.getPtr(), _TimeBlocks
.size(), keyTB
);
265 // get this timeBlock.
266 CTimeBlock
&timeBlock
= _TimeBlocks
[tbId
];
267 // get frame relative to this timeBlock.
268 sint frameRel
= frame
-timeBlock
.TimeOffset
;
270 clamp(frameRel
, 0, 255);
271 // get the key in this timeBlock.
273 keyIdRel
= searchLowerBound(timeBlock
.Times
.getPtr(), timeBlock
.Times
.size(), (uint8
)frameRel
);
275 // Get the Frame and Value of Key0.
276 uint frameKey0
= timeBlock
.TimeOffset
+ timeBlock
.Times
[keyIdRel
];
277 // this is the key to evaluate
278 keyId0
= timeBlock
.KeyOffset
+ keyIdRel
;
281 // Interpolate with next key
282 //=====================
284 // If not the last Key
290 // If last key of the timeBlock, get the first time of the next timeBlock.
291 if( keyIdRel
+1 >= timeBlock
.Times
.size() )
293 nlassert(tbId
+1<_TimeBlocks
.size());
294 frameKey1
= _TimeBlocks
[tbId
+1].TimeOffset
;
298 frameKey1
= timeBlock
.TimeOffset
+ timeBlock
.Times
[keyIdRel
+1];
302 float time0
= frameKey0
*_DeltaTime
;
303 float time1
= frameKey1
*_DeltaTime
;
306 float t
= (localTime
-time0
);
307 // If difference is one frame, optimize.
308 if(frameKey1
-frameKey0
==1)
314 // store this interp value.
317 return EvalInterpolate
;
319 // else (last key of anim), just eval this key.
324 // ***************************************************************************
325 void CTrackSampledCommon::applySampleDivisorCommon(uint sampleDivisor
, std::vector
<uint32
> &keepKeys
)
327 nlassert(sampleDivisor
>=2);
331 NB: to be faster, if we have multiple timeblock (rare, cause implies the anim>8.5 sec), the
332 number of time block is kept after this process, either if it could be lowered.
333 NB: for same reason, the first and last key of each timeBlock is kept to be simpler, and to avoid bug
334 in searchLowerBound() (we must keep first key of each timeBlock)
340 // **** build the key indices to keep
341 static std::vector
<uint32
> blockKeepStart
;
342 static std::vector
<uint32
> blockKeepEnd
;
343 blockKeepStart
.resize(_TimeBlocks
.size());
344 blockKeepEnd
.resize(_TimeBlocks
.size());
345 // must Keep the first and last key.
347 for(i
=0;i
<_TimeBlocks
.size();i
++)
349 CTimeBlock
&timeBlock
= _TimeBlocks
[i
];
351 // keep track of the start new key for this block
352 blockKeepStart
[i
]= (uint32
)keepKeys
.size();
354 for(j
=0;j
<timeBlock
.Times
.size();j
++)
356 // get the time of this key
357 uint keyTime
= timeBlock
.Times
[j
] + timeBlock
.TimeOffset
;
358 // if the diff time with last inserted key is >= than the sampleDivisor, add it!
359 if( (keyTime
- lastKeyTime
>= sampleDivisor
) ||
360 // add it also if it is the first or last key of the block
361 (j
==0 || j
==timeBlock
.Times
.size()-1) )
363 lastKeyTime
= keyTime
;
364 keepKeys
.push_back(j
+timeBlock
.KeyOffset
);
368 // keep track of the end (not included) new key for this block
369 blockKeepEnd
[i
]= (uint32
)keepKeys
.size();
372 // **** rebuild the TimeBlocks
373 for(i
=0;i
<_TimeBlocks
.size();i
++)
375 CTimeBlock
&timeBlock
= _TimeBlocks
[i
];
376 uint keepStart
= blockKeepStart
[i
];
377 uint keepEnd
= blockKeepEnd
[i
];
379 NLMISC::CObjectVector
<uint8
, false> newKeys
;
380 newKeys
.resize(keepEnd
-keepStart
);
381 for(uint j
=0;j
<newKeys
.size();j
++)
383 newKeys
[j
]= timeBlock
.Times
[keepKeys
[keepStart
+j
]-timeBlock
.KeyOffset
];
386 timeBlock
.Times
= newKeys
;
387 // change the key offset!
388 timeBlock
.KeyOffset
= keepStart
;