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