Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / bone.cpp
blob98d26c6ce1ed5dce511753dcc1044d09bf05d999
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/3d/bone.h"
20 #include "nel/3d/anim_ctrl.h"
21 #include "nel/misc/hierarchical_timer.h"
23 #ifdef DEBUG_NEW
24 #define new DEBUG_NEW
25 #endif
27 namespace NL3D
31 // ***************************************************************************
32 // ***************************************************************************
33 // CBoneBase
34 // ***************************************************************************
35 // ***************************************************************************
38 static const CVector UnitScale(1,1,1);
41 // ***************************************************************************
42 CBoneBase::CBoneBase() : DefaultPos(CVector(0,0,0)), DefaultRotEuler(CVector(0,0,0)),
43 DefaultScale(UnitScale), DefaultPivot(CVector(0,0,0)), SkinScale(UnitScale)
45 FatherId= -1;
46 UnheritScale= true;
47 // Default: never disable.
48 LodDisableDistance= 0.f;
52 // ***************************************************************************
53 void CBoneBase::serial(NLMISC::IStream &f)
56 Version 2:
57 - SkinScale
58 Version 1:
59 - LodDisableDistance
61 sint ver= f.serialVersion(2);
63 f.serial(Name);
64 f.serial(InvBindPos);
65 f.serial(FatherId);
66 f.serial(UnheritScale);
68 if(ver>=1)
69 f.serial(LodDisableDistance);
70 else
72 // Default: never disable.
73 LodDisableDistance= 0.f;
76 f.serial(DefaultPos);
77 f.serial(DefaultRotEuler);
78 f.serial(DefaultRotQuat);
79 f.serial(DefaultScale);
80 f.serial(DefaultPivot);
82 if(ver>=2)
83 f.serial(SkinScale);
87 // ***************************************************************************
88 // ***************************************************************************
89 // CBone
90 // ***************************************************************************
91 // ***************************************************************************
94 // ***************************************************************************
95 CBone::CBone(CBoneBase *boneBase)
97 nlassert(boneBase);
98 _BoneBase= boneBase;
100 // IAnimatable.
101 IAnimatable::resize(AnimValueLast);
103 ITransformable::setTransformMode(ITransformable::RotQuat);
104 ITransformable::setPos( _BoneBase->DefaultPos.getDefaultValue() );
105 ITransformable::setRotQuat( _BoneBase->DefaultRotQuat.getDefaultValue() );
106 ITransformable::setScale( _BoneBase->DefaultScale.getDefaultValue() );
107 ITransformable::setPivot( _BoneBase->DefaultPivot.getDefaultValue() );
109 // By default, the bone is not binded to a channelMixer.
110 _PosChannelId= -1;
111 _RotEulerChannelId= -1;
112 _RotQuatChannelId= -1;
113 _ScaleChannelId= -1;
114 _PivotChannelId= -1;
116 // No animCtrl by default
117 _AnimCtrl= NULL;
119 // Get default BoneBase SkinScale
120 _SkinScale= _BoneBase->SkinScale;
123 // ***************************************************************************
124 ITrack* CBone::getDefaultTrack (uint valueId)
126 nlassert(_BoneBase);
128 // what value ?
129 switch (valueId)
131 case PosValue: return &_BoneBase->DefaultPos;
132 case RotEulerValue: return &_BoneBase->DefaultRotEuler;
133 case RotQuatValue: return &_BoneBase->DefaultRotQuat;
134 case ScaleValue: return &_BoneBase->DefaultScale;
135 case PivotValue: return &_BoneBase->DefaultPivot;
138 // No, only ITrnasformable values!
139 nlstop;
140 // Deriver note: else call BaseClass::getDefaultTrack(valueId);
142 return NULL;
145 // ***************************************************************************
146 void CBone::registerToChannelMixer(CChannelMixer *chanMixer, const std::string &prefix)
148 // For CBone, channels are detailled.
149 // Bkup each channelId (for disable).
150 _PosChannelId= addValue(chanMixer, PosValue, OwnerBit, prefix, true);
151 _RotEulerChannelId= addValue(chanMixer, RotEulerValue, OwnerBit, prefix, true);
152 _RotQuatChannelId= addValue(chanMixer, RotQuatValue, OwnerBit, prefix, true);
153 _ScaleChannelId= addValue(chanMixer, ScaleValue, OwnerBit, prefix, true);
154 _PivotChannelId= addValue(chanMixer, PivotValue, OwnerBit, prefix, true);
156 // Deriver note: if necessary, call BaseClass::registerToChannelMixer(chanMixer, prefix);
159 // ***************************************************************************
160 void CBone::compute(CBone *parent, const CMatrix &rootMatrix, CSkeletonModel *skeletonForAnimCtrl)
162 // compute is called typically 800 time per frame.
163 #ifdef NL_DEBUG
164 nlassert(_BoneBase);
165 #endif
167 // get/compute our local matrix
168 const CMatrix &localMatrix= getMatrix();
170 // Compute LocalSkeletonMatrix.
171 // Root case?
172 if(!parent)
174 _LocalSkeletonMatrix= localMatrix;
176 // Else, son case, take world matrix from parent.
177 else
179 // UnheritScale case.
180 if(_BoneBase->UnheritScale)
182 CMatrix invScaleComp;
183 CVector fatherScale;
184 CVector trans;
186 /* Optim note:
187 the scale is rarely ==1, so don't optimize case where it may be.
190 // retrieve our translation
191 localMatrix.getPos(trans);
192 // retrieve scale from our father.
193 parent->getScale(fatherScale);
194 // inverse this scale.
195 fatherScale.x= 1.0f / fatherScale.x;
196 fatherScale.y= 1.0f / fatherScale.y;
197 fatherScale.z= 1.0f / fatherScale.z;
199 // Compute InverseScale compensation:
200 // with UnheritScale, formula per bone should be T*Sf-1*P*R*S*P-1.
201 // But getMatrix() return T*P*R*S*P-1.
202 // So we must compute T*Sf-1*T-1, in order to get wanted result.
203 invScaleComp.setScale(fatherScale);
204 // Faster compute of the translation part: just "trans + fatherScale MUL -trans" where MUL is comp mul
205 trans.x-= fatherScale.x * trans.x;
206 trans.y-= fatherScale.y * trans.y;
207 trans.z-= fatherScale.z * trans.z;
208 invScaleComp.setPos(trans);
211 // And finally, we got ParentWM * T*Sf-1*P*R*S*P-1.
212 // Do: _LocalSkeletonMatrix= parent->_LocalSkeletonMatrix * invScaleComp * localMatrix
213 static CMatrix tmp;
214 tmp.setMulMatrixNoProj( parent->_LocalSkeletonMatrix, invScaleComp );
215 _LocalSkeletonMatrix.setMulMatrixNoProj( tmp, localMatrix );
217 // Normal case.
218 else
220 // Do: _LocalSkeletonMatrix= parent->_LocalSkeletonMatrix * localMatrix
221 _LocalSkeletonMatrix.setMulMatrixNoProj( parent->_LocalSkeletonMatrix, localMatrix );
225 // Compute WorldMatrix. Do: _WorldMatrix= rootMatrix * _LocalSkeletonMatrix
226 _WorldMatrix.setMulMatrixNoProj( rootMatrix, _LocalSkeletonMatrix );
228 // Compute BoneSkinMatrix. Easier of no SkinScale
229 if(_SkinScale==UnitScale)
231 // Do: _BoneSkinMatrix= _LocalSkeletonMatrix * _BoneBase->InvBindPos
232 _BoneSkinMatrix.setMulMatrixNoProj( _LocalSkeletonMatrix, _BoneBase->InvBindPos );
234 else
236 // Do: _BoneSkinMatrix= _LocalSkeletonMatrix * SkinScale * _BoneBase->InvBindPos
237 CMatrix tmp;
238 CMatrix scaleMat;
239 scaleMat.setScale(_SkinScale);
240 tmp.setMulMatrixNoProj(_LocalSkeletonMatrix, scaleMat);
241 _BoneSkinMatrix.setMulMatrixNoProj( tmp, _BoneBase->InvBindPos );
244 // When compute is done, do extra user ctrl?
245 if(_AnimCtrl && skeletonForAnimCtrl)
246 _AnimCtrl->execute(skeletonForAnimCtrl, this);
250 // ***************************************************************************
251 void CBone::interpolateBoneSkinMatrix(const CMatrix &otherMatrix, float interp)
253 CMatrix &curMatrix= _BoneSkinMatrix;
255 // interpolate rot/scale. Just interpolate basis vectors
256 CVector fatherI= otherMatrix.getI();
257 CVector curI= curMatrix.getI();
258 curI= fatherI*(1-interp) + curI*interp;
259 CVector fatherJ= otherMatrix.getJ();
260 CVector curJ= curMatrix.getJ();
261 curJ= fatherJ*(1-interp) + curJ*interp;
262 CVector fatherK= otherMatrix.getK();
263 CVector curK= curMatrix.getK();
264 curK= fatherK*(1-interp) + curK*interp;
265 // replace rotation
266 curMatrix.setRot(curI, curJ, curK);
268 // interpolate pos
269 CVector fatherPos= otherMatrix.getPos();
270 CVector curPos= curMatrix.getPos();
271 curPos= fatherPos*(1-interp) + curPos*interp;
272 curMatrix.setPos(curPos);
276 // ***************************************************************************
277 void CBone::lodEnableChannels(CChannelMixer *chanMixer, bool enable)
279 nlassert(chanMixer);
281 // Lod Enable channels if they are correclty registered to the channelMixer.
282 if( _PosChannelId>=0 )
283 chanMixer->lodEnableChannel(_PosChannelId, enable);
284 if( _RotEulerChannelId>=0 )
285 chanMixer->lodEnableChannel(_RotEulerChannelId, enable);
286 if( _RotQuatChannelId>=0 )
287 chanMixer->lodEnableChannel(_RotQuatChannelId, enable);
288 if( _ScaleChannelId>=0 )
289 chanMixer->lodEnableChannel(_ScaleChannelId, enable);
290 if( _PivotChannelId>=0 )
291 chanMixer->lodEnableChannel(_PivotChannelId, enable);
295 // ***************************************************************************
296 void CBone::setSkinScale(CVector &skinScale)
298 _SkinScale= skinScale;
302 } // NL3D