Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / track_sampled_quat.cpp
blobd49d63aa7cc56b33e89c97b0be8e814d650fbef4
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
19 #include "nel/misc/quat.h"
20 #include "nel/misc/common.h"
21 #include "nel/3d/track_sampled_quat.h"
22 #include "nel/3d/track_sampled_quat_small_header.h"
24 using namespace NLMISC;
25 using namespace std;
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 namespace NL3D
34 // ***************************************************************************
35 // ***************************************************************************
36 // Quaternion compression
37 // ***************************************************************************
38 // ***************************************************************************
40 const double NL3D_OO32767= 1.0f/32767;
41 const double NL3D_OO65535= 1.0f/65535;
43 #ifdef NL3D_TSQ_ALLOW_QUAT_COMPRESS
44 // ***************************************************************************
45 void CQuatPack::pack(const CQuat &quat)
48 This is the most precise/faster compression we can have. Some other tries have been made.
50 - deducing w from x,y,z is possible with w= 1-sqrt(x^2+y^2+z^2) (with tradeoff of the W sign)
51 but very not precise.
52 - Transform the quaternion to an AxisAngle is possible, but slower (some cos/sin or LUT).
53 Axis is encoded with sint16, and angle is encoded with uint16.
54 - The same than above, but encode the axis as X/Y only, and deduce Z from
55 them, is possible but precision problems arise.
57 You can see that the operation "deduce a 3/4 member from unit length rule" is definetly not precise.
59 Hence this simpler but workable way.
62 // normalize the quaterion.
63 CQuatD nquat= quat;
64 nquat.normalize();
66 sint ax= (sint)floor(nquat.x * 32767 + 0.5);
67 sint ay= (sint)floor(nquat.y * 32767 + 0.5);
68 sint az= (sint)floor(nquat.z * 32767 + 0.5);
69 sint aw= (sint)floor(nquat.w * 32767 + 0.5);
70 clamp(ax, -32767, 32767);
71 clamp(ay, -32767, 32767);
72 clamp(az, -32767, 32767);
73 clamp(aw, -32767, 32767);
74 x= ax;
75 y= ay;
76 z= az;
77 w= aw;
80 // ***************************************************************************
81 void CQuatPack::unpack(CQuat &quat)
83 // unpack x/y/z.
84 CQuatD quatD;
85 quatD.x= x * NL3D_OO32767;
86 quatD.y= y * NL3D_OO32767;
87 quatD.z= z * NL3D_OO32767;
88 quatD.w= w * NL3D_OO32767;
89 quatD.normalize();
91 quat= quatD;
93 #endif
96 // ***************************************************************************
97 // ***************************************************************************
98 // CTrackSampledQuat
99 // ***************************************************************************
100 // ***************************************************************************
103 // ***************************************************************************
104 CTrackSampledQuat::CTrackSampledQuat()
108 // ***************************************************************************
109 CTrackSampledQuat::~CTrackSampledQuat()
113 // ***************************************************************************
114 void CTrackSampledQuat::serial(NLMISC::IStream &f)
117 Version 1:
118 - split class with base CTrackSampledCommon (must add a version in it).
119 Version 0:
120 - base version.
122 sint ver= f.serialVersion(1);
124 if( ver<=0 )
126 // serial Time infos, directly in CTrackSampledCommon
127 f.serial(_LoopMode);
128 f.serial(_BeginTime);
129 f.serial(_EndTime) ;
130 f.serial(_TotalRange);
131 f.serial(_OOTotalRange);
132 f.serial(_DeltaTime);
133 f.serial(_OODeltaTime);
134 f.serial(_TimeBlocks);
136 else
138 // serial Time infos.
139 CTrackSampledCommon::serialCommon(f);
142 // serial Keys.
143 f.serial(_Keys);
146 // ***************************************************************************
147 void CTrackSampledQuat::build(const std::vector<uint16> &timeList, const std::vector<CQuat> &keyList,
148 float beginTime, float endTime)
150 nlassert( endTime>beginTime || (beginTime==endTime && keyList.size()<=1) );
151 nlassert( keyList.size()==timeList.size() );
152 uint i;
154 // reset.
155 uint numKeys= (uint)keyList.size();
156 _Keys.clear();
157 _TimeBlocks.clear();
159 // Build Common time information
160 CTrackSampledCommon::buildCommon(timeList, beginTime, endTime);
163 // Compute All Key values.
164 //===================
165 _Keys.resize(numKeys);
166 for(i=0; i<numKeys;i++)
168 _Keys[i].pack(keyList[i]);
173 // ***************************************************************************
174 const IAnimatedValue &CTrackSampledQuat::eval (const TAnimationTime& date, CAnimatedValueBlock &avBlock)
176 /* IF YOU CHANGE THIS CODE, CHANGE too CTrackSampledQuatSmallHeader
179 // Eval time, and get key interpolation info
180 uint keyId0;
181 uint keyId1;
182 float interpValue;
183 TEvalType evalType= evalTime(date, _Keys.size(), keyId0, keyId1, interpValue);
185 // Discard?
186 if( evalType==EvalDiscard )
187 return avBlock.ValQuat;
188 // One Key? easy, and quit.
189 else if( evalType==EvalKey0 )
191 _Keys[keyId0].unpack(avBlock.ValQuat.Value);
193 // interpolate
194 else if( evalType==EvalInterpolate )
196 CQuatPack valueKey0= _Keys[keyId0];
197 CQuatPack valueKey1= _Keys[keyId1];
199 // If the 2 keys have same value, just unpack.
200 if(valueKey0 == valueKey1)
202 valueKey0.unpack(avBlock.ValQuat.Value);
204 // else interpolate
205 else
207 // unpack key value.
208 CQuat quat0, quat1;
209 valueKey0.unpack(quat0);
210 valueKey1.unpack(quat1);
212 // interpolate
213 avBlock.ValQuat.Value= CQuat::slerp(quat0, quat1, interpValue);
216 else
218 nlstop;
221 return avBlock.ValQuat;
224 // ***************************************************************************
225 void CTrackSampledQuat::applySampleDivisor(uint sampleDivisor)
227 if(sampleDivisor<=1)
228 return;
230 // **** build the key indices to keep, and rebuild the timeBlocks
231 static std::vector<uint32> keepKeys;
232 applySampleDivisorCommon(sampleDivisor, keepKeys);
234 // **** rebuild the keys
235 NLMISC::CObjectVector<CQuatPack, false> newKeys;
236 newKeys.resize((uint32)keepKeys.size());
237 for(uint i=0;i<newKeys.size();i++)
239 newKeys[i]= _Keys[keepKeys[i]];
241 // copy
242 _Keys= newKeys;
244 // TestYoyo
245 /*nlinfo("ANIMQUAT:\t%d\t%d\t%d\t%d", sizeof(*this), _TimeBlocks.size(),
246 _TimeBlocks.size()?_TimeBlocks[0].Times.size():0,
247 _Keys.size() * sizeof(CQuatPack));*/
251 // ***************************************************************************
252 bool CTrackSampledQuat::applyTrackQuatHeaderCompressionPass0(class CTrackSampleCounter &quatCounter)
254 // if there is more than 1 timeBlock, fails
255 if(_TimeBlocks.size()>1)
256 return false;
258 // Support only 255 keys and not 256!!! cause _NumKeys is encoded in 8 bits!
259 if(_Keys.size()>=256)
260 return false;
262 // if the number of keys ovveride the uint16 limit, abort
263 if(_Keys.size()+quatCounter.NumKeys > 65536)
264 return false;
266 // Search if the Track header is the same as one of the quatCounter.
267 // NB: O(N*N) but quatCounter.TrackHeaders should be very small
268 uint headerIndex;
269 for(headerIndex=0;headerIndex<quatCounter.TrackHeaders.size();headerIndex++)
271 CTrackSampleHeader &tsh= quatCounter.TrackHeaders[headerIndex];
272 if( tsh.LoopMode == _LoopMode &&
273 tsh.BeginTime == _BeginTime &&
274 tsh.EndTime == _EndTime &&
275 tsh.TotalRange == _TotalRange &&
276 tsh.OOTotalRange == _OOTotalRange &&
277 tsh.DeltaTime == _DeltaTime &&
278 tsh.OODeltaTime == _OODeltaTime )
279 break;
281 if(headerIndex==quatCounter.TrackHeaders.size())
283 // then must increment the TrackHeaders. must not ovverride the uint8 limit
284 if(quatCounter.TrackHeaders.size()==256)
285 return false;
286 else
288 nlassert(quatCounter.TrackHeaders.size()<256);
289 CTrackSampleHeader tsh;
290 tsh.LoopMode = _LoopMode;
291 tsh.BeginTime = _BeginTime;
292 tsh.EndTime = _EndTime;
293 tsh.TotalRange = _TotalRange;
294 tsh.OOTotalRange = _OOTotalRange;
295 tsh.DeltaTime = _DeltaTime;
296 tsh.OODeltaTime = _OODeltaTime;
297 quatCounter.TrackHeaders.push_back(tsh);
300 // else ok, one Header match
302 // increment the number of keys in the packed data
303 quatCounter.NumKeys+= _Keys.size();
305 // at least this track can be compressed
306 return true;
309 // ***************************************************************************
310 ITrack *CTrackSampledQuat::applyTrackQuatHeaderCompressionPass1(uint &globalKeyOffset, class CTrackSamplePack &quatPacker)
312 // if there is more than 1 timeBlock, fails
313 if(_TimeBlocks.size()>1)
314 return NULL;
316 // Support only 255 keys and not 256!!! cause _NumKeys is encoded in 8 bits!
317 if(_Keys.size()>=256)
318 return NULL;
320 // if the number of keys ovveride the uint16 limit, abort
321 if(_Keys.size()+globalKeyOffset > 65536)
322 return NULL;
324 // Search if the Track header is the same as one of the quatPacker.
325 // NB: O(N*N) but quatPacker.TrackHeaders should be very small
326 uint headerIndex;
327 for(headerIndex=0;headerIndex<quatPacker.TrackHeaders.size();headerIndex++)
329 CTrackSampleHeader &tsh= quatPacker.TrackHeaders[headerIndex];
330 if( tsh.LoopMode == _LoopMode &&
331 tsh.BeginTime == _BeginTime &&
332 tsh.EndTime == _EndTime &&
333 tsh.TotalRange == _TotalRange &&
334 tsh.OOTotalRange == _OOTotalRange &&
335 tsh.DeltaTime == _DeltaTime &&
336 tsh.OODeltaTime == _OODeltaTime )
337 break;
339 if(headerIndex==quatPacker.TrackHeaders.size())
341 return NULL;
344 // OK! this track can be converted to a CTrackSampledQuatSmallHeader
345 uint keyIndex= globalKeyOffset;
347 // increment the number of keys in the packed data
348 globalKeyOffset+= _Keys.size();
350 // **** fill the packer struct
351 uint i;
352 for(i=0;i<_Keys.size();i++)
354 quatPacker.Times[keyIndex+i]= _TimeBlocks[0].Times[i];
355 quatPacker.Keys[keyIndex+i]= _Keys[i];
358 // **** Build the compressed quat, and return it
359 return new CTrackSampledQuatSmallHeader(&quatPacker, (uint8)headerIndex, (uint8)_Keys.size(), keyIndex);
363 } // NL3D