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