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