]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CSkinnedMesh.cpp
Drop obsolete configuration macros
[irrlicht.git] / source / Irrlicht / CSkinnedMesh.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "IrrCompileConfig.h"\r
6 #include "CSkinnedMesh.h"\r
7 #include "CBoneSceneNode.h"\r
8 #include "IAnimatedMeshSceneNode.h"\r
9 #include "os.h"\r
10 \r
11 namespace\r
12 {\r
13         // Frames must always be increasing, so we remove objects where this isn't the case\r
14         // return number of kicked keys\r
15         template <class T> // T = objects containing a "frame" variable\r
16         irr::u32 dropBadKeys(irr::core::array<T>& array)\r
17         {\r
18                 if (array.size()<2)\r
19                         return 0;\r
20 \r
21                 irr::u32 n=1;   // new index\r
22                 for(irr::u32 j=1;j<array.size();++j)\r
23                 {\r
24                         if (array[j].frame < array[n-1].frame)\r
25                                 continue; //bad frame, unneeded and may cause problems\r
26                         if ( n != j )\r
27                                 array[n] = array[j];\r
28                         ++n;\r
29                 }\r
30                 irr::u32 d = array.size()-n; // remove already copied keys\r
31                 if ( d > 0 )\r
32                 {\r
33                         array.erase(n, d);\r
34                 }\r
35                 return d;\r
36         }\r
37 \r
38         // drop identical middle keys - we only need the first and last\r
39         // return number of kicked keys\r
40         template <class T, typename Cmp> // Cmp = comparison for keys of type T\r
41         irr::u32 dropMiddleKeys(irr::core::array<T>& array, Cmp & cmp)\r
42         {\r
43                 if ( array.size() < 3 )\r
44                         return 0;\r
45 \r
46                 irr::u32 s = 0;         // old index for current key\r
47                 irr::u32 n = 1; // new index for next key\r
48                 for(irr::u32 j=1;j<array.size();++j)\r
49                 {\r
50                         if ( cmp(array[j], array[s]) )\r
51                                 continue;       // same key, handle later\r
52 \r
53                         if ( j > s+1 ) // had there been identical keys?\r
54                                 array[n++] = array[j-1]; // keep the last\r
55                         array[n++] = array[j]; // keep the new one\r
56                         s = j;\r
57                 }\r
58                 if ( array.size() > s+1 ) // identical keys at the array end?\r
59                         array[n++] = array[array.size()-1]; // keep the last\r
60 \r
61                 irr::u32 d = array.size()-n; // remove already copied keys\r
62                 if ( d > 0 )\r
63                 {\r
64                         array.erase(n, d);\r
65                 }\r
66                 return d;\r
67         }\r
68 \r
69         bool identicalPos(const irr::scene::ISkinnedMesh::SPositionKey& a, const irr::scene::ISkinnedMesh::SPositionKey& b)\r
70         {\r
71                 return a.position == b.position;\r
72         }\r
73 \r
74         bool identicalScale(const irr::scene::ISkinnedMesh::SScaleKey& a, const irr::scene::ISkinnedMesh::SScaleKey& b)\r
75         {\r
76                 return a.scale == b.scale;\r
77         }\r
78 \r
79         bool identicalRotation(const irr::scene::ISkinnedMesh::SRotationKey& a, const irr::scene::ISkinnedMesh::SRotationKey& b)\r
80         {\r
81                 return a.rotation == b.rotation;\r
82         }\r
83 }\r
84 \r
85 namespace irr\r
86 {\r
87 namespace scene\r
88 {\r
89 \r
90 \r
91 //! constructor\r
92 CSkinnedMesh::CSkinnedMesh()\r
93 : SkinningBuffers(0), EndFrame(0.f), FramesPerSecond(25.f),\r
94         LastAnimatedFrame(-1), SkinnedLastFrame(false),\r
95         InterpolationMode(EIM_LINEAR),\r
96         HasAnimation(false), PreparedForSkinning(false),\r
97         AnimateNormals(true), HardwareSkinning(false)\r
98 {\r
99         #ifdef _DEBUG\r
100         setDebugName("CSkinnedMesh");\r
101         #endif\r
102 \r
103         SkinningBuffers=&LocalBuffers;\r
104 }\r
105 \r
106 \r
107 //! destructor\r
108 CSkinnedMesh::~CSkinnedMesh()\r
109 {\r
110         for (u32 i=0; i<AllJoints.size(); ++i)\r
111                 delete AllJoints[i];\r
112 \r
113         for (u32 j=0; j<LocalBuffers.size(); ++j)\r
114         {\r
115                 if (LocalBuffers[j])\r
116                         LocalBuffers[j]->drop();\r
117         }\r
118 }\r
119 \r
120 \r
121 //! returns the amount of frames in milliseconds.\r
122 //! If the amount is 1, it is a static (=non animated) mesh.\r
123 u32 CSkinnedMesh::getFrameCount() const\r
124 {\r
125         return core::floor32(EndFrame+1.f);\r
126 }\r
127 \r
128 \r
129 //! Gets the default animation speed of the animated mesh.\r
130 /** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */\r
131 f32 CSkinnedMesh::getAnimationSpeed() const\r
132 {\r
133         return FramesPerSecond;\r
134 }\r
135 \r
136 \r
137 //! Gets the frame count of the animated mesh.\r
138 /** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated.\r
139 The actual speed is set in the scene node the mesh is instantiated in.*/\r
140 void CSkinnedMesh::setAnimationSpeed(f32 fps)\r
141 {\r
142         FramesPerSecond=fps;\r
143 }\r
144 \r
145 \r
146 //! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level.\r
147 IMesh* CSkinnedMesh::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)\r
148 {\r
149         //animate(frame,startFrameLoop, endFrameLoop);\r
150         if (frame==-1)\r
151                 return this;\r
152 \r
153         animateMesh((f32)frame, 1.0f);\r
154         skinMesh();\r
155         return this;\r
156 }\r
157 \r
158 \r
159 //--------------------------------------------------------------------------\r
160 //                      Keyframe Animation\r
161 //--------------------------------------------------------------------------\r
162 \r
163 \r
164 //! Animates this mesh's joints based on frame input\r
165 //! blend: {0-old position, 1-New position}\r
166 void CSkinnedMesh::animateMesh(f32 frame, f32 blend)\r
167 {\r
168         if (!HasAnimation || LastAnimatedFrame==frame)\r
169                 return;\r
170 \r
171         LastAnimatedFrame=frame;\r
172         SkinnedLastFrame=false;\r
173 \r
174         if (blend<=0.f)\r
175                 return; //No need to animate\r
176 \r
177         for (u32 i=0; i<AllJoints.size(); ++i)\r
178         {\r
179                 //The joints can be animated here with no input from their\r
180                 //parents, but for setAnimationMode extra checks are needed\r
181                 //to their parents\r
182                 SJoint *joint = AllJoints[i];\r
183 \r
184                 const core::vector3df oldPosition = joint->Animatedposition;\r
185                 const core::vector3df oldScale = joint->Animatedscale;\r
186                 const core::quaternion oldRotation = joint->Animatedrotation;\r
187 \r
188                 core::vector3df position = oldPosition;\r
189                 core::vector3df scale = oldScale;\r
190                 core::quaternion rotation = oldRotation;\r
191 \r
192                 getFrameData(frame, joint,\r
193                                 position, joint->positionHint,\r
194                                 scale, joint->scaleHint,\r
195                                 rotation, joint->rotationHint);\r
196 \r
197                 if (blend==1.0f)\r
198                 {\r
199                         //No blending needed\r
200                         joint->Animatedposition = position;\r
201                         joint->Animatedscale = scale;\r
202                         joint->Animatedrotation = rotation;\r
203                 }\r
204                 else\r
205                 {\r
206                         //Blend animation\r
207                         joint->Animatedposition = core::lerp(oldPosition, position, blend);\r
208                         joint->Animatedscale = core::lerp(oldScale, scale, blend);\r
209                         joint->Animatedrotation.slerp(oldRotation, rotation, blend);\r
210                 }\r
211         }\r
212 \r
213         //Note:\r
214         //LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for\r
215         //one render (to play two animations at the same time) LocalAnimatedMatrix only needs to be built once.\r
216         //a call to buildAllLocalAnimatedMatrices is needed before skinning the mesh, and before the user gets the joints to move\r
217 \r
218         //----------------\r
219         // Temp!\r
220         buildAllLocalAnimatedMatrices();\r
221         //-----------------\r
222 \r
223         updateBoundingBox();\r
224 }\r
225 \r
226 \r
227 void CSkinnedMesh::buildAllLocalAnimatedMatrices()\r
228 {\r
229         for (u32 i=0; i<AllJoints.size(); ++i)\r
230         {\r
231                 SJoint *joint = AllJoints[i];\r
232 \r
233                 //Could be faster:\r
234 \r
235                 if (joint->UseAnimationFrom &&\r
236                         (joint->UseAnimationFrom->PositionKeys.size() ||\r
237                          joint->UseAnimationFrom->ScaleKeys.size() ||\r
238                          joint->UseAnimationFrom->RotationKeys.size() ))\r
239                 {\r
240                         joint->GlobalSkinningSpace=false;\r
241 \r
242                         // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched to getMatrix_transposed instead of getMatrix for downward compatibility.\r
243                         //                                                                 Not tested so far if this was correct or wrong before quaternion fix!\r
244                         joint->Animatedrotation.getMatrix_transposed(joint->LocalAnimatedMatrix);\r
245 \r
246                         // --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() ---\r
247                         f32 *m1 = joint->LocalAnimatedMatrix.pointer();\r
248                         core::vector3df &Pos = joint->Animatedposition;\r
249                         m1[0] += Pos.X*m1[3];\r
250                         m1[1] += Pos.Y*m1[3];\r
251                         m1[2] += Pos.Z*m1[3];\r
252                         m1[4] += Pos.X*m1[7];\r
253                         m1[5] += Pos.Y*m1[7];\r
254                         m1[6] += Pos.Z*m1[7];\r
255                         m1[8] += Pos.X*m1[11];\r
256                         m1[9] += Pos.Y*m1[11];\r
257                         m1[10] += Pos.Z*m1[11];\r
258                         m1[12] += Pos.X*m1[15];\r
259                         m1[13] += Pos.Y*m1[15];\r
260                         m1[14] += Pos.Z*m1[15];\r
261                         // -----------------------------------\r
262 \r
263                         if (joint->ScaleKeys.size())\r
264                         {\r
265                                 /*\r
266                                 core::matrix4 scaleMatrix;\r
267                                 scaleMatrix.setScale(joint->Animatedscale);\r
268                                 joint->LocalAnimatedMatrix *= scaleMatrix;\r
269                                 */\r
270 \r
271                                 // -------- joint->LocalAnimatedMatrix *= scaleMatrix -----------------\r
272                                 core::matrix4& mat = joint->LocalAnimatedMatrix;\r
273                                 mat[0] *= joint->Animatedscale.X;\r
274                                 mat[1] *= joint->Animatedscale.X;\r
275                                 mat[2] *= joint->Animatedscale.X;\r
276                                 mat[3] *= joint->Animatedscale.X;\r
277                                 mat[4] *= joint->Animatedscale.Y;\r
278                                 mat[5] *= joint->Animatedscale.Y;\r
279                                 mat[6] *= joint->Animatedscale.Y;\r
280                                 mat[7] *= joint->Animatedscale.Y;\r
281                                 mat[8] *= joint->Animatedscale.Z;\r
282                                 mat[9] *= joint->Animatedscale.Z;\r
283                                 mat[10] *= joint->Animatedscale.Z;\r
284                                 mat[11] *= joint->Animatedscale.Z;\r
285                                 // -----------------------------------\r
286                         }\r
287                 }\r
288                 else\r
289                 {\r
290                         joint->LocalAnimatedMatrix=joint->LocalMatrix;\r
291                 }\r
292         }\r
293         SkinnedLastFrame=false;\r
294 }\r
295 \r
296 \r
297 void CSkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint)\r
298 {\r
299         if (!joint)\r
300         {\r
301                 for (u32 i=0; i<RootJoints.size(); ++i)\r
302                         buildAllGlobalAnimatedMatrices(RootJoints[i], 0);\r
303                 return;\r
304         }\r
305         else\r
306         {\r
307                 // Find global matrix...\r
308                 if (!parentJoint || joint->GlobalSkinningSpace)\r
309                         joint->GlobalAnimatedMatrix = joint->LocalAnimatedMatrix;\r
310                 else\r
311                         joint->GlobalAnimatedMatrix = parentJoint->GlobalAnimatedMatrix * joint->LocalAnimatedMatrix;\r
312         }\r
313 \r
314         for (u32 j=0; j<joint->Children.size(); ++j)\r
315                 buildAllGlobalAnimatedMatrices(joint->Children[j], joint);\r
316 }\r
317 \r
318 \r
319 void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint,\r
320                                 core::vector3df &position, s32 &positionHint,\r
321                                 core::vector3df &scale, s32 &scaleHint,\r
322                                 core::quaternion &rotation, s32 &rotationHint)\r
323 {\r
324         s32 foundPositionIndex = -1;\r
325         s32 foundScaleIndex = -1;\r
326         s32 foundRotationIndex = -1;\r
327 \r
328         if (joint->UseAnimationFrom)\r
329         {\r
330                 const core::array<SPositionKey> &PositionKeys=joint->UseAnimationFrom->PositionKeys;\r
331                 const core::array<SScaleKey> &ScaleKeys=joint->UseAnimationFrom->ScaleKeys;\r
332                 const core::array<SRotationKey> &RotationKeys=joint->UseAnimationFrom->RotationKeys;\r
333 \r
334                 if (PositionKeys.size())\r
335                 {\r
336                         foundPositionIndex = -1;\r
337 \r
338                         //Test the Hints...\r
339                         if (positionHint>=0 && (u32)positionHint < PositionKeys.size())\r
340                         {\r
341                                 //check this hint\r
342                                 if (positionHint>0 && PositionKeys[positionHint].frame>=frame && PositionKeys[positionHint-1].frame<frame )\r
343                                         foundPositionIndex=positionHint;\r
344                                 else if (positionHint+1 < (s32)PositionKeys.size())\r
345                                 {\r
346                                         //check the next index\r
347                                         if ( PositionKeys[positionHint+1].frame>=frame &&\r
348                                                         PositionKeys[positionHint+0].frame<frame)\r
349                                         {\r
350                                                 positionHint++;\r
351                                                 foundPositionIndex=positionHint;\r
352                                         }\r
353                                 }\r
354                         }\r
355 \r
356                         //The hint test failed, do a full scan...\r
357                         if (foundPositionIndex==-1)\r
358                         {\r
359                                 for (u32 i=0; i<PositionKeys.size(); ++i)\r
360                                 {\r
361                                         if (PositionKeys[i].frame >= frame) //Keys should to be sorted by frame\r
362                                         {\r
363                                                 foundPositionIndex=i;\r
364                                                 positionHint=i;\r
365                                                 break;\r
366                                         }\r
367                                 }\r
368                         }\r
369 \r
370                         //Do interpolation...\r
371                         if (foundPositionIndex!=-1)\r
372                         {\r
373                                 if (InterpolationMode==EIM_CONSTANT || foundPositionIndex==0)\r
374                                 {\r
375                                         position = PositionKeys[foundPositionIndex].position;\r
376                                 }\r
377                                 else if (InterpolationMode==EIM_LINEAR)\r
378                                 {\r
379                                         const SPositionKey& KeyA = PositionKeys[foundPositionIndex];\r
380                                         const SPositionKey& KeyB = PositionKeys[foundPositionIndex-1];\r
381 \r
382                                         const f32 fd1 = frame - KeyA.frame;\r
383                                         const f32 fd2 = KeyB.frame - frame;\r
384                                         position = ((KeyB.position-KeyA.position)/(fd1+fd2))*fd1 + KeyA.position;\r
385                                 }\r
386                         }\r
387                 }\r
388 \r
389                 //------------------------------------------------------------\r
390 \r
391                 if (ScaleKeys.size())\r
392                 {\r
393                         foundScaleIndex = -1;\r
394 \r
395                         //Test the Hints...\r
396                         if (scaleHint>=0 && (u32)scaleHint < ScaleKeys.size())\r
397                         {\r
398                                 //check this hint\r
399                                 if (scaleHint>0 && ScaleKeys[scaleHint].frame>=frame && ScaleKeys[scaleHint-1].frame<frame )\r
400                                         foundScaleIndex=scaleHint;\r
401                                 else if (scaleHint+1 < (s32)ScaleKeys.size())\r
402                                 {\r
403                                         //check the next index\r
404                                         if ( ScaleKeys[scaleHint+1].frame>=frame &&\r
405                                                         ScaleKeys[scaleHint+0].frame<frame)\r
406                                         {\r
407                                                 scaleHint++;\r
408                                                 foundScaleIndex=scaleHint;\r
409                                         }\r
410                                 }\r
411                         }\r
412 \r
413 \r
414                         //The hint test failed, do a full scan...\r
415                         if (foundScaleIndex==-1)\r
416                         {\r
417                                 for (u32 i=0; i<ScaleKeys.size(); ++i)\r
418                                 {\r
419                                         if (ScaleKeys[i].frame >= frame) //Keys should to be sorted by frame\r
420                                         {\r
421                                                 foundScaleIndex=i;\r
422                                                 scaleHint=i;\r
423                                                 break;\r
424                                         }\r
425                                 }\r
426                         }\r
427 \r
428                         //Do interpolation...\r
429                         if (foundScaleIndex!=-1)\r
430                         {\r
431                                 if (InterpolationMode==EIM_CONSTANT || foundScaleIndex==0)\r
432                                 {\r
433                                         scale = ScaleKeys[foundScaleIndex].scale;\r
434                                 }\r
435                                 else if (InterpolationMode==EIM_LINEAR)\r
436                                 {\r
437                                         const SScaleKey& KeyA = ScaleKeys[foundScaleIndex];\r
438                                         const SScaleKey& KeyB = ScaleKeys[foundScaleIndex-1];\r
439 \r
440                                         const f32 fd1 = frame - KeyA.frame;\r
441                                         const f32 fd2 = KeyB.frame - frame;\r
442                                         scale = ((KeyB.scale-KeyA.scale)/(fd1+fd2))*fd1 + KeyA.scale;\r
443                                 }\r
444                         }\r
445                 }\r
446 \r
447                 //-------------------------------------------------------------\r
448 \r
449                 if (RotationKeys.size())\r
450                 {\r
451                         foundRotationIndex = -1;\r
452 \r
453                         //Test the Hints...\r
454                         if (rotationHint>=0 && (u32)rotationHint < RotationKeys.size())\r
455                         {\r
456                                 //check this hint\r
457                                 if (rotationHint>0 && RotationKeys[rotationHint].frame>=frame && RotationKeys[rotationHint-1].frame<frame )\r
458                                         foundRotationIndex=rotationHint;\r
459                                 else if (rotationHint+1 < (s32)RotationKeys.size())\r
460                                 {\r
461                                         //check the next index\r
462                                         if ( RotationKeys[rotationHint+1].frame>=frame &&\r
463                                                         RotationKeys[rotationHint+0].frame<frame)\r
464                                         {\r
465                                                 rotationHint++;\r
466                                                 foundRotationIndex=rotationHint;\r
467                                         }\r
468                                 }\r
469                         }\r
470 \r
471 \r
472                         //The hint test failed, do a full scan...\r
473                         if (foundRotationIndex==-1)\r
474                         {\r
475                                 for (u32 i=0; i<RotationKeys.size(); ++i)\r
476                                 {\r
477                                         if (RotationKeys[i].frame >= frame) //Keys should be sorted by frame\r
478                                         {\r
479                                                 foundRotationIndex=i;\r
480                                                 rotationHint=i;\r
481                                                 break;\r
482                                         }\r
483                                 }\r
484                         }\r
485 \r
486                         //Do interpolation...\r
487                         if (foundRotationIndex!=-1)\r
488                         {\r
489                                 if (InterpolationMode==EIM_CONSTANT || foundRotationIndex==0)\r
490                                 {\r
491                                         rotation = RotationKeys[foundRotationIndex].rotation;\r
492                                 }\r
493                                 else if (InterpolationMode==EIM_LINEAR)\r
494                                 {\r
495                                         const SRotationKey& KeyA = RotationKeys[foundRotationIndex];\r
496                                         const SRotationKey& KeyB = RotationKeys[foundRotationIndex-1];\r
497 \r
498                                         const f32 fd1 = frame - KeyA.frame;\r
499                                         const f32 fd2 = KeyB.frame - frame;\r
500                                         const f32 t = fd1/(fd1+fd2);\r
501 \r
502                                         /*\r
503                                         f32 t = 0;\r
504                                         if (KeyA.frame!=KeyB.frame)\r
505                                                 t = (frame-KeyA.frame) / (KeyB.frame - KeyA.frame);\r
506                                         */\r
507 \r
508                                         rotation.slerp(KeyA.rotation, KeyB.rotation, t);\r
509                                 }\r
510                         }\r
511                 }\r
512         }\r
513 }\r
514 \r
515 //--------------------------------------------------------------------------\r
516 //                              Software Skinning\r
517 //--------------------------------------------------------------------------\r
518 \r
519 //! Preforms a software skin on this mesh based of joint positions\r
520 void CSkinnedMesh::skinMesh()\r
521 {\r
522         if (!HasAnimation || SkinnedLastFrame)\r
523                 return;\r
524 \r
525         //----------------\r
526         // This is marked as "Temp!".  A shiny dubloon to whomever can tell me why.\r
527         buildAllGlobalAnimatedMatrices();\r
528         //-----------------\r
529 \r
530         SkinnedLastFrame=true;\r
531         if (!HardwareSkinning)\r
532         {\r
533                 //Software skin....\r
534                 u32 i;\r
535 \r
536                 //rigid animation\r
537                 for (i=0; i<AllJoints.size(); ++i)\r
538                 {\r
539                         for (u32 j=0; j<AllJoints[i]->AttachedMeshes.size(); ++j)\r
540                         {\r
541                                 SSkinMeshBuffer* Buffer=(*SkinningBuffers)[ AllJoints[i]->AttachedMeshes[j] ];\r
542                                 Buffer->Transformation=AllJoints[i]->GlobalAnimatedMatrix;\r
543                         }\r
544                 }\r
545 \r
546                 //clear skinning helper array\r
547                 for (i=0; i<Vertices_Moved.size(); ++i)\r
548                         for (u32 j=0; j<Vertices_Moved[i].size(); ++j)\r
549                                 Vertices_Moved[i][j]=false;\r
550 \r
551                 //skin starting with the root joints\r
552                 for (i=0; i<RootJoints.size(); ++i)\r
553                         skinJoint(RootJoints[i], 0);\r
554 \r
555                 for (i=0; i<SkinningBuffers->size(); ++i)\r
556                         (*SkinningBuffers)[i]->setDirty(EBT_VERTEX);\r
557         }\r
558         updateBoundingBox();\r
559 }\r
560 \r
561 \r
562 void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)\r
563 {\r
564         if (joint->Weights.size())\r
565         {\r
566                 //Find this joints pull on vertices...\r
567                 core::matrix4 jointVertexPull(core::matrix4::EM4CONST_NOTHING);\r
568                 jointVertexPull.setbyproduct(joint->GlobalAnimatedMatrix, joint->GlobalInversedMatrix);\r
569 \r
570                 core::vector3df thisVertexMove, thisNormalMove;\r
571 \r
572                 core::array<scene::SSkinMeshBuffer*> &buffersUsed=*SkinningBuffers;\r
573 \r
574                 //Skin Vertices Positions and Normals...\r
575                 for (u32 i=0; i<joint->Weights.size(); ++i)\r
576                 {\r
577                         SWeight& weight = joint->Weights[i];\r
578 \r
579                         // Pull this vertex...\r
580                         jointVertexPull.transformVect(thisVertexMove, weight.StaticPos);\r
581 \r
582                         if (AnimateNormals)\r
583                                 jointVertexPull.rotateVect(thisNormalMove, weight.StaticNormal);\r
584 \r
585                         if (! (*(weight.Moved)) )\r
586                         {\r
587                                 *(weight.Moved) = true;\r
588 \r
589                                 buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos = thisVertexMove * weight.strength;\r
590 \r
591                                 if (AnimateNormals)\r
592                                         buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal = thisNormalMove * weight.strength;\r
593 \r
594                                 //*(weight._Pos) = thisVertexMove * weight.strength;\r
595                         }\r
596                         else\r
597                         {\r
598                                 buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos += thisVertexMove * weight.strength;\r
599 \r
600                                 if (AnimateNormals)\r
601                                         buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal += thisNormalMove * weight.strength;\r
602 \r
603                                 //*(weight._Pos) += thisVertexMove * weight.strength;\r
604                         }\r
605 \r
606                         buffersUsed[weight.buffer_id]->boundingBoxNeedsRecalculated();\r
607                 }\r
608         }\r
609 \r
610         //Skin all children\r
611         for (u32 j=0; j<joint->Children.size(); ++j)\r
612                 skinJoint(joint->Children[j], joint);\r
613 }\r
614 \r
615 \r
616 E_ANIMATED_MESH_TYPE CSkinnedMesh::getMeshType() const\r
617 {\r
618         return EAMT_SKINNED;\r
619 }\r
620 \r
621 \r
622 //! Gets joint count.\r
623 u32 CSkinnedMesh::getJointCount() const\r
624 {\r
625         return AllJoints.size();\r
626 }\r
627 \r
628 \r
629 //! Gets the name of a joint.\r
630 const c8* CSkinnedMesh::getJointName(u32 number) const\r
631 {\r
632         if (number >= AllJoints.size())\r
633                 return 0;\r
634         return AllJoints[number]->Name.c_str();\r
635 }\r
636 \r
637 \r
638 //! Gets a joint number from its name\r
639 s32 CSkinnedMesh::getJointNumber(const c8* name) const\r
640 {\r
641         for (u32 i=0; i<AllJoints.size(); ++i)\r
642         {\r
643                 if (AllJoints[i]->Name == name)\r
644                         return i;\r
645         }\r
646 \r
647         return -1;\r
648 }\r
649 \r
650 \r
651 //! returns amount of mesh buffers.\r
652 u32 CSkinnedMesh::getMeshBufferCount() const\r
653 {\r
654         return LocalBuffers.size();\r
655 }\r
656 \r
657 \r
658 //! returns pointer to a mesh buffer\r
659 IMeshBuffer* CSkinnedMesh::getMeshBuffer(u32 nr) const\r
660 {\r
661         if (nr < LocalBuffers.size())\r
662                 return LocalBuffers[nr];\r
663         else\r
664                 return 0;\r
665 }\r
666 \r
667 \r
668 //! Returns pointer to a mesh buffer which fits a material\r
669 IMeshBuffer* CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const\r
670 {\r
671         for (u32 i=0; i<LocalBuffers.size(); ++i)\r
672         {\r
673                 if (LocalBuffers[i]->getMaterial() == material)\r
674                         return LocalBuffers[i];\r
675         }\r
676         return 0;\r
677 }\r
678 \r
679 \r
680 //! returns an axis aligned bounding box\r
681 const core::aabbox3d<f32>& CSkinnedMesh::getBoundingBox() const\r
682 {\r
683         return BoundingBox;\r
684 }\r
685 \r
686 \r
687 //! set user axis aligned bounding box\r
688 void CSkinnedMesh::setBoundingBox( const core::aabbox3df& box)\r
689 {\r
690         BoundingBox = box;\r
691 }\r
692 \r
693 \r
694 //! sets a flag of all contained materials to a new value\r
695 void CSkinnedMesh::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)\r
696 {\r
697         for (u32 i=0; i<LocalBuffers.size(); ++i)\r
698                 LocalBuffers[i]->Material.setFlag(flag,newvalue);\r
699 }\r
700 \r
701 \r
702 //! set the hardware mapping hint, for driver\r
703 void CSkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,\r
704                 E_BUFFER_TYPE buffer)\r
705 {\r
706         for (u32 i=0; i<LocalBuffers.size(); ++i)\r
707                 LocalBuffers[i]->setHardwareMappingHint(newMappingHint, buffer);\r
708 }\r
709 \r
710 \r
711 //! flags the meshbuffer as changed, reloads hardware buffers\r
712 void CSkinnedMesh::setDirty(E_BUFFER_TYPE buffer)\r
713 {\r
714         for (u32 i=0; i<LocalBuffers.size(); ++i)\r
715                 LocalBuffers[i]->setDirty(buffer);\r
716 }\r
717 \r
718 \r
719 //! uses animation from another mesh\r
720 bool CSkinnedMesh::useAnimationFrom(const ISkinnedMesh *mesh)\r
721 {\r
722         bool unmatched=false;\r
723 \r
724         for(u32 i=0;i<AllJoints.size();++i)\r
725         {\r
726                 SJoint *joint=AllJoints[i];\r
727                 joint->UseAnimationFrom=0;\r
728 \r
729                 if (joint->Name=="")\r
730                         unmatched=true;\r
731                 else\r
732                 {\r
733                         for(u32 j=0;j<mesh->getAllJoints().size();++j)\r
734                         {\r
735                                 SJoint *otherJoint=mesh->getAllJoints()[j];\r
736                                 if (joint->Name==otherJoint->Name)\r
737                                 {\r
738                                         joint->UseAnimationFrom=otherJoint;\r
739                                 }\r
740                         }\r
741                         if (!joint->UseAnimationFrom)\r
742                                 unmatched=true;\r
743                 }\r
744         }\r
745 \r
746         checkForAnimation();\r
747 \r
748         return !unmatched;\r
749 }\r
750 \r
751 \r
752 //!Update Normals when Animating\r
753 //!False= Don't animate them, faster\r
754 //!True= Update normals (default)\r
755 void CSkinnedMesh::updateNormalsWhenAnimating(bool on)\r
756 {\r
757         AnimateNormals = on;\r
758 }\r
759 \r
760 \r
761 //!Sets Interpolation Mode\r
762 void CSkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode)\r
763 {\r
764         InterpolationMode = mode;\r
765 }\r
766 \r
767 \r
768 core::array<scene::SSkinMeshBuffer*> &CSkinnedMesh::getMeshBuffers()\r
769 {\r
770         return LocalBuffers;\r
771 }\r
772 \r
773 \r
774 core::array<CSkinnedMesh::SJoint*> &CSkinnedMesh::getAllJoints()\r
775 {\r
776         return AllJoints;\r
777 }\r
778 \r
779 \r
780 const core::array<CSkinnedMesh::SJoint*> &CSkinnedMesh::getAllJoints() const\r
781 {\r
782         return AllJoints;\r
783 }\r
784 \r
785 \r
786 //! (This feature is not implemented in irrlicht yet)\r
787 bool CSkinnedMesh::setHardwareSkinning(bool on)\r
788 {\r
789         if (HardwareSkinning!=on)\r
790         {\r
791                 if (on)\r
792                 {\r
793 \r
794                         //set mesh to static pose...\r
795                         for (u32 i=0; i<AllJoints.size(); ++i)\r
796                         {\r
797                                 SJoint *joint=AllJoints[i];\r
798                                 for (u32 j=0; j<joint->Weights.size(); ++j)\r
799                                 {\r
800                                         const u16 buffer_id=joint->Weights[j].buffer_id;\r
801                                         const u32 vertex_id=joint->Weights[j].vertex_id;\r
802                                         LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = joint->Weights[j].StaticPos;\r
803                                         LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = joint->Weights[j].StaticNormal;\r
804                                         LocalBuffers[buffer_id]->boundingBoxNeedsRecalculated();\r
805                                 }\r
806                         }\r
807                 }\r
808 \r
809                 HardwareSkinning=on;\r
810         }\r
811         return HardwareSkinning;\r
812 }\r
813 \r
814 void CSkinnedMesh::refreshJointCache()\r
815 {\r
816         //copy cache from the mesh...\r
817         for (u32 i=0; i<AllJoints.size(); ++i)\r
818         {\r
819                 SJoint *joint=AllJoints[i];\r
820                 for (u32 j=0; j<joint->Weights.size(); ++j)\r
821                 {\r
822                         const u16 buffer_id=joint->Weights[j].buffer_id;\r
823                         const u32 vertex_id=joint->Weights[j].vertex_id;\r
824                         joint->Weights[j].StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos;\r
825                         joint->Weights[j].StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal;\r
826                 }\r
827         }\r
828 }\r
829 \r
830 void CSkinnedMesh::resetAnimation()\r
831 {\r
832         //copy from the cache to the mesh...\r
833         for (u32 i=0; i<AllJoints.size(); ++i)\r
834         {\r
835                 SJoint *joint=AllJoints[i];\r
836                 for (u32 j=0; j<joint->Weights.size(); ++j)\r
837                 {\r
838                         const u16 buffer_id=joint->Weights[j].buffer_id;\r
839                         const u32 vertex_id=joint->Weights[j].vertex_id;\r
840                         LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = joint->Weights[j].StaticPos;\r
841                         LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = joint->Weights[j].StaticNormal;\r
842                 }\r
843         }\r
844         SkinnedLastFrame = false;\r
845         LastAnimatedFrame = -1;\r
846 }\r
847 \r
848 void CSkinnedMesh::calculateGlobalMatrices(SJoint *joint,SJoint *parentJoint)\r
849 {\r
850         if (!joint && parentJoint) // bit of protection from endless loops\r
851                 return;\r
852 \r
853         //Go through the root bones\r
854         if (!joint)\r
855         {\r
856                 for (u32 i=0; i<RootJoints.size(); ++i)\r
857                         calculateGlobalMatrices(RootJoints[i],0);\r
858                 return;\r
859         }\r
860 \r
861         if (!parentJoint)\r
862                 joint->GlobalMatrix = joint->LocalMatrix;\r
863         else\r
864                 joint->GlobalMatrix = parentJoint->GlobalMatrix * joint->LocalMatrix;\r
865 \r
866         joint->LocalAnimatedMatrix=joint->LocalMatrix;\r
867         joint->GlobalAnimatedMatrix=joint->GlobalMatrix;\r
868 \r
869         if (joint->GlobalInversedMatrix.isIdentity())//might be pre calculated\r
870         {\r
871                 joint->GlobalInversedMatrix = joint->GlobalMatrix;\r
872                 joint->GlobalInversedMatrix.makeInverse(); // slow\r
873         }\r
874 \r
875         for (u32 j=0; j<joint->Children.size(); ++j)\r
876                 calculateGlobalMatrices(joint->Children[j],joint);\r
877         SkinnedLastFrame=false;\r
878 }\r
879 \r
880 \r
881 void CSkinnedMesh::checkForAnimation()\r
882 {\r
883         u32 i,j;\r
884         //Check for animation...\r
885         HasAnimation = false;\r
886         for(i=0;i<AllJoints.size();++i)\r
887         {\r
888                 if (AllJoints[i]->UseAnimationFrom)\r
889                 {\r
890                         if (AllJoints[i]->UseAnimationFrom->PositionKeys.size() ||\r
891                                 AllJoints[i]->UseAnimationFrom->ScaleKeys.size() ||\r
892                                 AllJoints[i]->UseAnimationFrom->RotationKeys.size() )\r
893                         {\r
894                                 HasAnimation = true;\r
895                         }\r
896                 }\r
897         }\r
898 \r
899         //meshes with weights, are still counted as animated for ragdolls, etc\r
900         if (!HasAnimation)\r
901         {\r
902                 for(i=0;i<AllJoints.size();++i)\r
903                 {\r
904                         if (AllJoints[i]->Weights.size())\r
905                                 HasAnimation = true;\r
906                 }\r
907         }\r
908 \r
909         if (HasAnimation)\r
910         {\r
911                 //--- Find the length of the animation ---\r
912                 EndFrame=0;\r
913                 for(i=0;i<AllJoints.size();++i)\r
914                 {\r
915                         if (AllJoints[i]->UseAnimationFrom)\r
916                         {\r
917                                 if (AllJoints[i]->UseAnimationFrom->PositionKeys.size())\r
918                                         if (AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame > EndFrame)\r
919                                                 EndFrame=AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame;\r
920 \r
921                                 if (AllJoints[i]->UseAnimationFrom->ScaleKeys.size())\r
922                                         if (AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame > EndFrame)\r
923                                                 EndFrame=AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame;\r
924 \r
925                                 if (AllJoints[i]->UseAnimationFrom->RotationKeys.size())\r
926                                         if (AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame > EndFrame)\r
927                                                 EndFrame=AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame;\r
928                         }\r
929                 }\r
930         }\r
931 \r
932         if (HasAnimation && !PreparedForSkinning)\r
933         {\r
934                 PreparedForSkinning=true;\r
935 \r
936                 //check for bugs:\r
937                 for(i=0; i < AllJoints.size(); ++i)\r
938                 {\r
939                         SJoint *joint = AllJoints[i];\r
940                         for (j=0; j<joint->Weights.size(); ++j)\r
941                         {\r
942                                 const u16 buffer_id=joint->Weights[j].buffer_id;\r
943                                 const u32 vertex_id=joint->Weights[j].vertex_id;\r
944 \r
945                                 //check for invalid ids\r
946                                 if (buffer_id>=LocalBuffers.size())\r
947                                 {\r
948                                         os::Printer::log("Skinned Mesh: Weight buffer id too large", ELL_WARNING);\r
949                                         joint->Weights[j].buffer_id = joint->Weights[j].vertex_id =0;\r
950                                 }\r
951                                 else if (vertex_id>=LocalBuffers[buffer_id]->getVertexCount())\r
952                                 {\r
953                                         os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING);\r
954                                         joint->Weights[j].buffer_id = joint->Weights[j].vertex_id =0;\r
955                                 }\r
956                         }\r
957                 }\r
958 \r
959                 //An array used in skinning\r
960 \r
961                 for (i=0; i<Vertices_Moved.size(); ++i)\r
962                         for (j=0; j<Vertices_Moved[i].size(); ++j)\r
963                                 Vertices_Moved[i][j] = false;\r
964 \r
965                 // For skinning: cache weight values for speed\r
966 \r
967                 for (i=0; i<AllJoints.size(); ++i)\r
968                 {\r
969                         SJoint *joint = AllJoints[i];\r
970                         for (j=0; j<joint->Weights.size(); ++j)\r
971                         {\r
972                                 const u16 buffer_id=joint->Weights[j].buffer_id;\r
973                                 const u32 vertex_id=joint->Weights[j].vertex_id;\r
974 \r
975                                 joint->Weights[j].Moved = &Vertices_Moved[buffer_id] [vertex_id];\r
976                                 joint->Weights[j].StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos;\r
977                                 joint->Weights[j].StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal;\r
978 \r
979                                 //joint->Weights[j]._Pos=&Buffers[buffer_id]->getVertex(vertex_id)->Pos;\r
980                         }\r
981                 }\r
982 \r
983                 // normalize weights\r
984                 normalizeWeights();\r
985         }\r
986         SkinnedLastFrame=false;\r
987 }\r
988 \r
989 //! called by loader after populating with mesh and bone data\r
990 void CSkinnedMesh::finalize()\r
991 {\r
992         os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG);\r
993         u32 i;\r
994 \r
995         // Make sure we recalc the next frame\r
996         LastAnimatedFrame=-1;\r
997         SkinnedLastFrame=false;\r
998 \r
999         //calculate bounding box\r
1000         for (i=0; i<LocalBuffers.size(); ++i)\r
1001         {\r
1002                 LocalBuffers[i]->recalculateBoundingBox();\r
1003         }\r
1004 \r
1005         if (AllJoints.size() || RootJoints.size())\r
1006         {\r
1007                 // populate AllJoints or RootJoints, depending on which is empty\r
1008                 if (!RootJoints.size())\r
1009                 {\r
1010 \r
1011                         for(u32 CheckingIdx=0; CheckingIdx < AllJoints.size(); ++CheckingIdx)\r
1012                         {\r
1013 \r
1014                                 bool foundParent=false;\r
1015                                 for(i=0; i < AllJoints.size(); ++i)\r
1016                                 {\r
1017                                         for(u32 n=0; n < AllJoints[i]->Children.size(); ++n)\r
1018                                         {\r
1019                                                 if (AllJoints[i]->Children[n] == AllJoints[CheckingIdx])\r
1020                                                         foundParent=true;\r
1021                                         }\r
1022                                 }\r
1023 \r
1024                                 if (!foundParent)\r
1025                                         RootJoints.push_back(AllJoints[CheckingIdx]);\r
1026                         }\r
1027                 }\r
1028                 else\r
1029                 {\r
1030                         AllJoints=RootJoints;\r
1031                 }\r
1032         }\r
1033 \r
1034         for(i=0; i < AllJoints.size(); ++i)\r
1035         {\r
1036                 AllJoints[i]->UseAnimationFrom=AllJoints[i];\r
1037         }\r
1038 \r
1039         //Set array sizes...\r
1040 \r
1041         for (i=0; i<LocalBuffers.size(); ++i)\r
1042         {\r
1043                 Vertices_Moved.push_back( core::array<char>() );\r
1044                 Vertices_Moved[i].set_used(LocalBuffers[i]->getVertexCount());\r
1045         }\r
1046 \r
1047         checkForAnimation();\r
1048 \r
1049         if (HasAnimation)\r
1050         {\r
1051                 irr::u32 redundantPosKeys = 0;\r
1052                 irr::u32 unorderedPosKeys = 0;\r
1053                 irr::u32 redundantScaleKeys = 0;\r
1054                 irr::u32 unorderedScaleKeys = 0;\r
1055                 irr::u32 redundantRotationKeys = 0;\r
1056                 irr::u32 unorderedRotationKeys = 0;\r
1057 \r
1058                 //--- optimize and check keyframes ---\r
1059                 for(i=0;i<AllJoints.size();++i)\r
1060                 {\r
1061                         core::array<SPositionKey> &PositionKeys =AllJoints[i]->PositionKeys;\r
1062                         core::array<SScaleKey> &ScaleKeys = AllJoints[i]->ScaleKeys;\r
1063                         core::array<SRotationKey> &RotationKeys = AllJoints[i]->RotationKeys;\r
1064 \r
1065                         // redundant = identical middle keys - we only need the first and last frame\r
1066                         // unordered = frames which are out of order - we can't handle those\r
1067                         redundantPosKeys += dropMiddleKeys<SPositionKey>(PositionKeys, identicalPos);\r
1068                         unorderedPosKeys += dropBadKeys<SPositionKey>(PositionKeys);\r
1069                         redundantScaleKeys += dropMiddleKeys<SScaleKey>(ScaleKeys, identicalScale);\r
1070                         unorderedScaleKeys += dropBadKeys<SScaleKey>(ScaleKeys);\r
1071                         redundantRotationKeys += dropMiddleKeys<SRotationKey>(RotationKeys, identicalRotation);\r
1072                         unorderedRotationKeys += dropBadKeys<SRotationKey>(RotationKeys);\r
1073 \r
1074                         //Fill empty keyframe areas\r
1075                         if (PositionKeys.size())\r
1076                         {\r
1077                                 SPositionKey *Key;\r
1078                                 Key=&PositionKeys[0];//getFirst\r
1079                                 if (Key->frame!=0)\r
1080                                 {\r
1081                                         PositionKeys.push_front(*Key);\r
1082                                         Key=&PositionKeys[0];//getFirst\r
1083                                         Key->frame=0;\r
1084                                 }\r
1085 \r
1086                                 Key=&PositionKeys.getLast();\r
1087                                 if (Key->frame!=EndFrame)\r
1088                                 {\r
1089                                         PositionKeys.push_back(*Key);\r
1090                                         Key=&PositionKeys.getLast();\r
1091                                         Key->frame=EndFrame;\r
1092                                 }\r
1093                         }\r
1094 \r
1095                         if (ScaleKeys.size())\r
1096                         {\r
1097                                 SScaleKey *Key;\r
1098                                 Key=&ScaleKeys[0];//getFirst\r
1099                                 if (Key->frame!=0)\r
1100                                 {\r
1101                                         ScaleKeys.push_front(*Key);\r
1102                                         Key=&ScaleKeys[0];//getFirst\r
1103                                         Key->frame=0;\r
1104                                 }\r
1105 \r
1106                                 Key=&ScaleKeys.getLast();\r
1107                                 if (Key->frame!=EndFrame)\r
1108                                 {\r
1109                                         ScaleKeys.push_back(*Key);\r
1110                                         Key=&ScaleKeys.getLast();\r
1111                                         Key->frame=EndFrame;\r
1112                                 }\r
1113                         }\r
1114 \r
1115                         if (RotationKeys.size())\r
1116                         {\r
1117                                 SRotationKey *Key;\r
1118                                 Key=&RotationKeys[0];//getFirst\r
1119                                 if (Key->frame!=0)\r
1120                                 {\r
1121                                         RotationKeys.push_front(*Key);\r
1122                                         Key=&RotationKeys[0];//getFirst\r
1123                                         Key->frame=0;\r
1124                                 }\r
1125 \r
1126                                 Key=&RotationKeys.getLast();\r
1127                                 if (Key->frame!=EndFrame)\r
1128                                 {\r
1129                                         RotationKeys.push_back(*Key);\r
1130                                         Key=&RotationKeys.getLast();\r
1131                                         Key->frame=EndFrame;\r
1132                                 }\r
1133                         }\r
1134                 }\r
1135 \r
1136                 if ( redundantPosKeys > 0 )\r
1137                 {\r
1138                         os::Printer::log("Skinned Mesh - redundant position frames kicked:", core::stringc(redundantPosKeys).c_str(), ELL_DEBUG);\r
1139                 }\r
1140                 if ( unorderedPosKeys > 0 )\r
1141                 {\r
1142                         irr::os::Printer::log("Skinned Mesh - unsorted position frames kicked:", irr::core::stringc(unorderedPosKeys).c_str(), irr::ELL_DEBUG);\r
1143                 }\r
1144                 if ( redundantScaleKeys > 0 )\r
1145                 {\r
1146                         os::Printer::log("Skinned Mesh - redundant scale frames kicked:", core::stringc(redundantScaleKeys).c_str(), ELL_DEBUG);\r
1147                 }\r
1148                 if ( unorderedScaleKeys > 0 )\r
1149                 {\r
1150                         irr::os::Printer::log("Skinned Mesh - unsorted scale frames kicked:", irr::core::stringc(unorderedScaleKeys).c_str(), irr::ELL_DEBUG);\r
1151                 }\r
1152                 if ( redundantRotationKeys > 0 )\r
1153                 {\r
1154                         os::Printer::log("Skinned Mesh - redundant rotation frames kicked:", core::stringc(redundantRotationKeys).c_str(), ELL_DEBUG);\r
1155                 }\r
1156                 if ( unorderedRotationKeys > 0 )\r
1157                 {\r
1158                         irr::os::Printer::log("Skinned Mesh - unsorted rotation frames kicked:", irr::core::stringc(unorderedRotationKeys).c_str(), irr::ELL_DEBUG);\r
1159                 }\r
1160         }\r
1161 \r
1162         //Needed for animation and skinning...\r
1163 \r
1164         calculateGlobalMatrices(0,0);\r
1165 \r
1166         //animateMesh(0, 1);\r
1167         //buildAllLocalAnimatedMatrices();\r
1168         //buildAllGlobalAnimatedMatrices();\r
1169 \r
1170         //rigid animation for non animated meshes\r
1171         for (i=0; i<AllJoints.size(); ++i)\r
1172         {\r
1173                 for (u32 j=0; j<AllJoints[i]->AttachedMeshes.size(); ++j)\r
1174                 {\r
1175                         SSkinMeshBuffer* Buffer=(*SkinningBuffers)[ AllJoints[i]->AttachedMeshes[j] ];\r
1176                         Buffer->Transformation=AllJoints[i]->GlobalAnimatedMatrix;\r
1177                 }\r
1178         }\r
1179 \r
1180         //calculate bounding box\r
1181         if (LocalBuffers.empty())\r
1182                 BoundingBox.reset(0,0,0);\r
1183         else\r
1184         {\r
1185                 irr::core::aabbox3df bb(LocalBuffers[0]->BoundingBox);\r
1186                 LocalBuffers[0]->Transformation.transformBoxEx(bb);\r
1187                 BoundingBox.reset(bb);\r
1188 \r
1189                 for (u32 j=1; j<LocalBuffers.size(); ++j)\r
1190                 {\r
1191                         bb = LocalBuffers[j]->BoundingBox;\r
1192                         LocalBuffers[j]->Transformation.transformBoxEx(bb);\r
1193 \r
1194                         BoundingBox.addInternalBox(bb);\r
1195                 }\r
1196         }\r
1197 }\r
1198 \r
1199 \r
1200 void CSkinnedMesh::updateBoundingBox(void)\r
1201 {\r
1202         if(!SkinningBuffers)\r
1203                 return;\r
1204 \r
1205         core::array<SSkinMeshBuffer*> & buffer = *SkinningBuffers;\r
1206         BoundingBox.reset(0,0,0);\r
1207 \r
1208         if (!buffer.empty())\r
1209         {\r
1210                 for (u32 j=0; j<buffer.size(); ++j)\r
1211                 {\r
1212                         buffer[j]->recalculateBoundingBox();\r
1213                         core::aabbox3df bb = buffer[j]->BoundingBox;\r
1214                         buffer[j]->Transformation.transformBoxEx(bb);\r
1215 \r
1216                         BoundingBox.addInternalBox(bb);\r
1217                 }\r
1218         }\r
1219 }\r
1220 \r
1221 \r
1222 scene::SSkinMeshBuffer *CSkinnedMesh::addMeshBuffer()\r
1223 {\r
1224         scene::SSkinMeshBuffer *buffer=new scene::SSkinMeshBuffer();\r
1225         LocalBuffers.push_back(buffer);\r
1226         return buffer;\r
1227 }\r
1228 \r
1229 \r
1230 CSkinnedMesh::SJoint *CSkinnedMesh::addJoint(SJoint *parent)\r
1231 {\r
1232         SJoint *joint=new SJoint;\r
1233 \r
1234         AllJoints.push_back(joint);\r
1235         if (!parent)\r
1236         {\r
1237                 //Add root joints to array in finalize()\r
1238         }\r
1239         else\r
1240         {\r
1241                 //Set parent (Be careful of the mesh loader also setting the parent)\r
1242                 parent->Children.push_back(joint);\r
1243         }\r
1244 \r
1245         return joint;\r
1246 }\r
1247 \r
1248 \r
1249 CSkinnedMesh::SPositionKey *CSkinnedMesh::addPositionKey(SJoint *joint)\r
1250 {\r
1251         if (!joint)\r
1252                 return 0;\r
1253 \r
1254         joint->PositionKeys.push_back(SPositionKey());\r
1255         return &joint->PositionKeys.getLast();\r
1256 }\r
1257 \r
1258 \r
1259 CSkinnedMesh::SScaleKey *CSkinnedMesh::addScaleKey(SJoint *joint)\r
1260 {\r
1261         if (!joint)\r
1262                 return 0;\r
1263 \r
1264         joint->ScaleKeys.push_back(SScaleKey());\r
1265         return &joint->ScaleKeys.getLast();\r
1266 }\r
1267 \r
1268 \r
1269 CSkinnedMesh::SRotationKey *CSkinnedMesh::addRotationKey(SJoint *joint)\r
1270 {\r
1271         if (!joint)\r
1272                 return 0;\r
1273 \r
1274         joint->RotationKeys.push_back(SRotationKey());\r
1275         return &joint->RotationKeys.getLast();\r
1276 }\r
1277 \r
1278 \r
1279 CSkinnedMesh::SWeight *CSkinnedMesh::addWeight(SJoint *joint)\r
1280 {\r
1281         if (!joint)\r
1282                 return 0;\r
1283 \r
1284         joint->Weights.push_back(SWeight());\r
1285         return &joint->Weights.getLast();\r
1286 }\r
1287 \r
1288 \r
1289 bool CSkinnedMesh::isStatic()\r
1290 {\r
1291         return !HasAnimation;\r
1292 }\r
1293 \r
1294 \r
1295 void CSkinnedMesh::normalizeWeights()\r
1296 {\r
1297         // note: unsure if weights ids are going to be used.\r
1298 \r
1299         // Normalise the weights on bones....\r
1300 \r
1301         u32 i,j;\r
1302         core::array< core::array<f32> > verticesTotalWeight;\r
1303 \r
1304         verticesTotalWeight.reallocate(LocalBuffers.size());\r
1305         for (i=0; i<LocalBuffers.size(); ++i)\r
1306         {\r
1307                 verticesTotalWeight.push_back(core::array<f32>());\r
1308                 verticesTotalWeight[i].set_used(LocalBuffers[i]->getVertexCount());\r
1309         }\r
1310 \r
1311         for (i=0; i<verticesTotalWeight.size(); ++i)\r
1312                 for (j=0; j<verticesTotalWeight[i].size(); ++j)\r
1313                         verticesTotalWeight[i][j] = 0;\r
1314 \r
1315         for (i=0; i<AllJoints.size(); ++i)\r
1316         {\r
1317                 SJoint *joint=AllJoints[i];\r
1318                 for (j=0; j<joint->Weights.size(); ++j)\r
1319                 {\r
1320                         if (joint->Weights[j].strength<=0)//Check for invalid weights\r
1321                         {\r
1322                                 joint->Weights.erase(j);\r
1323                                 --j;\r
1324                         }\r
1325                         else\r
1326                         {\r
1327                                 verticesTotalWeight[joint->Weights[j].buffer_id] [joint->Weights[j].vertex_id] += joint->Weights[j].strength;\r
1328                         }\r
1329                 }\r
1330         }\r
1331 \r
1332         for (i=0; i<AllJoints.size(); ++i)\r
1333         {\r
1334                 SJoint *joint=AllJoints[i];\r
1335                 for (j=0; j< joint->Weights.size(); ++j)\r
1336                 {\r
1337                         const f32 total = verticesTotalWeight[joint->Weights[j].buffer_id] [joint->Weights[j].vertex_id];\r
1338                         if (total != 0 && total != 1)\r
1339                                 joint->Weights[j].strength /= total;\r
1340                 }\r
1341         }\r
1342 }\r
1343 \r
1344 \r
1345 void CSkinnedMesh::recoverJointsFromMesh(core::array<IBoneSceneNode*> &jointChildSceneNodes)\r
1346 {\r
1347         for (u32 i=0; i<AllJoints.size(); ++i)\r
1348         {\r
1349                 IBoneSceneNode* node=jointChildSceneNodes[i];\r
1350                 SJoint *joint=AllJoints[i];\r
1351                 node->setPosition(joint->LocalAnimatedMatrix.getTranslation());\r
1352                 node->setRotation(joint->LocalAnimatedMatrix.getRotationDegrees());\r
1353                 node->setScale(joint->LocalAnimatedMatrix.getScale());\r
1354 \r
1355                 node->positionHint=joint->positionHint;\r
1356                 node->scaleHint=joint->scaleHint;\r
1357                 node->rotationHint=joint->rotationHint;\r
1358 \r
1359                 node->updateAbsolutePosition();\r
1360         }\r
1361 }\r
1362 \r
1363 \r
1364 void CSkinnedMesh::transferJointsToMesh(const core::array<IBoneSceneNode*> &jointChildSceneNodes)\r
1365 {\r
1366         for (u32 i=0; i<AllJoints.size(); ++i)\r
1367         {\r
1368                 const IBoneSceneNode* const node=jointChildSceneNodes[i];\r
1369                 SJoint *joint=AllJoints[i];\r
1370 \r
1371                 joint->LocalAnimatedMatrix.setRotationDegrees(node->getRotation());\r
1372                 joint->LocalAnimatedMatrix.setTranslation(node->getPosition());\r
1373                 joint->LocalAnimatedMatrix *= core::matrix4().setScale(node->getScale());\r
1374 \r
1375                 joint->positionHint=node->positionHint;\r
1376                 joint->scaleHint=node->scaleHint;\r
1377                 joint->rotationHint=node->rotationHint;\r
1378 \r
1379                 joint->GlobalSkinningSpace=(node->getSkinningSpace()==EBSS_GLOBAL);\r
1380         }\r
1381         // Make sure we recalc the next frame\r
1382         LastAnimatedFrame=-1;\r
1383         SkinnedLastFrame=false;\r
1384 }\r
1385 \r
1386 \r
1387 void CSkinnedMesh::transferOnlyJointsHintsToMesh(const core::array<IBoneSceneNode*> &jointChildSceneNodes)\r
1388 {\r
1389         for (u32 i=0; i<AllJoints.size(); ++i)\r
1390         {\r
1391                 const IBoneSceneNode* const node=jointChildSceneNodes[i];\r
1392                 SJoint *joint=AllJoints[i];\r
1393 \r
1394                 joint->positionHint=node->positionHint;\r
1395                 joint->scaleHint=node->scaleHint;\r
1396                 joint->rotationHint=node->rotationHint;\r
1397         }\r
1398         SkinnedLastFrame=false;\r
1399 }\r
1400 \r
1401 \r
1402 void CSkinnedMesh::addJoints(core::array<IBoneSceneNode*> &jointChildSceneNodes,\r
1403                 IAnimatedMeshSceneNode* node, ISceneManager* smgr)\r
1404 {\r
1405         //Create new joints\r
1406         for (u32 i=0; i<AllJoints.size(); ++i)\r
1407         {\r
1408                 jointChildSceneNodes.push_back(new CBoneSceneNode(0, smgr, 0, i, AllJoints[i]->Name.c_str()));\r
1409         }\r
1410 \r
1411         //Match up parents\r
1412         for (u32 i=0; i<jointChildSceneNodes.size(); ++i)\r
1413         {\r
1414                 const SJoint* const joint=AllJoints[i]; //should be fine\r
1415 \r
1416                 s32 parentID=-1;\r
1417 \r
1418                 for (u32 j=0;(parentID==-1)&&(j<AllJoints.size());++j)\r
1419                 {\r
1420                         if (i!=j)\r
1421                         {\r
1422                                 const SJoint* const parentTest=AllJoints[j];\r
1423                                 for (u32 n=0; n<parentTest->Children.size(); ++n)\r
1424                                 {\r
1425                                         if (parentTest->Children[n]==joint)\r
1426                                         {\r
1427                                                 parentID=j;\r
1428                                                 break;\r
1429                                         }\r
1430                                 }\r
1431                         }\r
1432                 }\r
1433 \r
1434                 IBoneSceneNode* bone=jointChildSceneNodes[i];\r
1435                 if (parentID!=-1)\r
1436                         bone->setParent(jointChildSceneNodes[parentID]);\r
1437                 else\r
1438                         bone->setParent(node);\r
1439 \r
1440                 bone->drop();\r
1441         }\r
1442         SkinnedLastFrame=false;\r
1443 }\r
1444 \r
1445 \r
1446 void CSkinnedMesh::convertMeshToTangents()\r
1447 {\r
1448         // now calculate tangents\r
1449         for (u32 b=0; b < LocalBuffers.size(); ++b)\r
1450         {\r
1451                 if (LocalBuffers[b])\r
1452                 {\r
1453                         LocalBuffers[b]->convertToTangents();\r
1454 \r
1455                         const s32 idxCnt = LocalBuffers[b]->getIndexCount();\r
1456 \r
1457                         u16* idx = LocalBuffers[b]->getIndices();\r
1458                         video::S3DVertexTangents* v =\r
1459                                 (video::S3DVertexTangents*)LocalBuffers[b]->getVertices();\r
1460 \r
1461                         for (s32 i=0; i<idxCnt; i+=3)\r
1462                         {\r
1463                                 calculateTangents(\r
1464                                         v[idx[i+0]].Normal,\r
1465                                         v[idx[i+0]].Tangent,\r
1466                                         v[idx[i+0]].Binormal,\r
1467                                         v[idx[i+0]].Pos,\r
1468                                         v[idx[i+1]].Pos,\r
1469                                         v[idx[i+2]].Pos,\r
1470                                         v[idx[i+0]].TCoords,\r
1471                                         v[idx[i+1]].TCoords,\r
1472                                         v[idx[i+2]].TCoords);\r
1473 \r
1474                                 calculateTangents(\r
1475                                         v[idx[i+1]].Normal,\r
1476                                         v[idx[i+1]].Tangent,\r
1477                                         v[idx[i+1]].Binormal,\r
1478                                         v[idx[i+1]].Pos,\r
1479                                         v[idx[i+2]].Pos,\r
1480                                         v[idx[i+0]].Pos,\r
1481                                         v[idx[i+1]].TCoords,\r
1482                                         v[idx[i+2]].TCoords,\r
1483                                         v[idx[i+0]].TCoords);\r
1484 \r
1485                                 calculateTangents(\r
1486                                         v[idx[i+2]].Normal,\r
1487                                         v[idx[i+2]].Tangent,\r
1488                                         v[idx[i+2]].Binormal,\r
1489                                         v[idx[i+2]].Pos,\r
1490                                         v[idx[i+0]].Pos,\r
1491                                         v[idx[i+1]].Pos,\r
1492                                         v[idx[i+2]].TCoords,\r
1493                                         v[idx[i+0]].TCoords,\r
1494                                         v[idx[i+1]].TCoords);\r
1495                         }\r
1496                 }\r
1497         }\r
1498 }\r
1499 \r
1500 \r
1501 void CSkinnedMesh::calculateTangents(\r
1502         core::vector3df& normal,\r
1503         core::vector3df& tangent,\r
1504         core::vector3df& binormal,\r
1505         const core::vector3df& vt1, const core::vector3df& vt2, const core::vector3df& vt3, // vertices\r
1506         const core::vector2df& tc1, const core::vector2df& tc2, const core::vector2df& tc3) // texture coords\r
1507 {\r
1508         core::vector3df v1 = vt1 - vt2;\r
1509         core::vector3df v2 = vt3 - vt1;\r
1510         normal = v2.crossProduct(v1);\r
1511         normal.normalize();\r
1512 \r
1513         // binormal\r
1514 \r
1515         f32 deltaX1 = tc1.X - tc2.X;\r
1516         f32 deltaX2 = tc3.X - tc1.X;\r
1517         binormal = (v1 * deltaX2) - (v2 * deltaX1);\r
1518         binormal.normalize();\r
1519 \r
1520         // tangent\r
1521 \r
1522         f32 deltaY1 = tc1.Y - tc2.Y;\r
1523         f32 deltaY2 = tc3.Y - tc1.Y;\r
1524         tangent = (v1 * deltaY2) - (v2 * deltaY1);\r
1525         tangent.normalize();\r
1526 \r
1527         // adjust\r
1528 \r
1529         core::vector3df txb = tangent.crossProduct(binormal);\r
1530         if (txb.dotProduct(normal) < 0.0f)\r
1531         {\r
1532                 tangent *= -1.0f;\r
1533                 binormal *= -1.0f;\r
1534         }\r
1535 }\r
1536 \r
1537 \r
1538 } // end namespace scene\r
1539 } // end namespace irr\r