]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CAnimatedMeshSceneNode.cpp
Use swap_control from MESA and EXT before SGI
[irrlicht.git] / source / Irrlicht / CAnimatedMeshSceneNode.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 "CAnimatedMeshSceneNode.h"\r
6 #include "IVideoDriver.h"\r
7 #include "ISceneManager.h"\r
8 #include "S3DVertex.h"\r
9 #include "os.h"\r
10 #include "CSkinnedMesh.h"\r
11 #include "IDummyTransformationSceneNode.h"\r
12 #include "IBoneSceneNode.h"\r
13 #include "IMaterialRenderer.h"\r
14 #include "IMesh.h"\r
15 #include "IMeshCache.h"\r
16 #include "IAnimatedMesh.h"\r
17 #include "IFileSystem.h"\r
18 #include "quaternion.h"\r
19 \r
20 \r
21 namespace irr\r
22 {\r
23 namespace scene\r
24 {\r
25 \r
26 \r
27 //! constructor\r
28 CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh* mesh,\r
29                 ISceneNode* parent, ISceneManager* mgr, s32 id,\r
30                 const core::vector3df& position,\r
31                 const core::vector3df& rotation,\r
32                 const core::vector3df& scale)\r
33 : IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0),\r
34         StartFrame(0), EndFrame(0), FramesPerSecond(0.025f),\r
35         CurrentFrameNr(0.f), LastTimeMs(0),\r
36         TransitionTime(0), Transiting(0.f), TransitingBlend(0.f),\r
37         JointMode(EJUOR_NONE), JointsUsed(false),\r
38         Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false),\r
39         LoopCallBack(0), PassCount(0)\r
40 {\r
41         #ifdef _DEBUG\r
42         setDebugName("CAnimatedMeshSceneNode");\r
43         #endif\r
44 \r
45         setMesh(mesh);\r
46 }\r
47 \r
48 \r
49 //! destructor\r
50 CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode()\r
51 {\r
52         if (LoopCallBack)\r
53                 LoopCallBack->drop();\r
54 }\r
55 \r
56 \r
57 //! Sets the current frame. From now on the animation is played from this frame.\r
58 void CAnimatedMeshSceneNode::setCurrentFrame(f32 frame)\r
59 {\r
60         // if you pass an out of range value, we just clamp it\r
61         CurrentFrameNr = core::clamp ( frame, (f32)StartFrame, (f32)EndFrame );\r
62 \r
63         beginTransition(); //transit to this frame if enabled\r
64 }\r
65 \r
66 \r
67 //! Returns the currently displayed frame number.\r
68 f32 CAnimatedMeshSceneNode::getFrameNr() const\r
69 {\r
70         return CurrentFrameNr;\r
71 }\r
72 \r
73 \r
74 //! Get CurrentFrameNr and update transiting settings\r
75 void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)\r
76 {\r
77         if (Transiting!=0.f)\r
78         {\r
79                 TransitingBlend += (f32)(timeMs) * Transiting;\r
80                 if (TransitingBlend > 1.f)\r
81                 {\r
82                         Transiting=0.f;\r
83                         TransitingBlend=0.f;\r
84                 }\r
85         }\r
86 \r
87         if (StartFrame==EndFrame)\r
88         {\r
89                 CurrentFrameNr = (f32)StartFrame; //Support for non animated meshes\r
90         }\r
91         else if (Looping)\r
92         {\r
93                 // play animation looped\r
94                 CurrentFrameNr += timeMs * FramesPerSecond;\r
95 \r
96                 // We have no interpolation between EndFrame and StartFrame,\r
97                 // the last frame must be identical to first one with our current solution.\r
98                 if (FramesPerSecond > 0.f) //forwards...\r
99                 {\r
100                         if (CurrentFrameNr > EndFrame)\r
101                                 CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, (f32)(EndFrame-StartFrame));\r
102                 }\r
103                 else //backwards...\r
104                 {\r
105                         if (CurrentFrameNr < StartFrame)\r
106                                 CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, (f32)(EndFrame-StartFrame));\r
107                 }\r
108         }\r
109         else\r
110         {\r
111                 // play animation non looped\r
112 \r
113                 CurrentFrameNr += timeMs * FramesPerSecond;\r
114                 if (FramesPerSecond > 0.f) //forwards...\r
115                 {\r
116                         if (CurrentFrameNr > (f32)EndFrame)\r
117                         {\r
118                                 CurrentFrameNr = (f32)EndFrame;\r
119                                 if (LoopCallBack)\r
120                                         LoopCallBack->OnAnimationEnd(this);\r
121                         }\r
122                 }\r
123                 else //backwards...\r
124                 {\r
125                         if (CurrentFrameNr < (f32)StartFrame)\r
126                         {\r
127                                 CurrentFrameNr = (f32)StartFrame;\r
128                                 if (LoopCallBack)\r
129                                         LoopCallBack->OnAnimationEnd(this);\r
130                         }\r
131                 }\r
132         }\r
133 }\r
134 \r
135 \r
136 void CAnimatedMeshSceneNode::OnRegisterSceneNode()\r
137 {\r
138         if (IsVisible && Mesh)\r
139         {\r
140                 // because this node supports rendering of mixed mode meshes consisting of\r
141                 // transparent and solid material at the same time, we need to go through all\r
142                 // materials, check of what type they are and register this node for the right\r
143                 // render pass according to that.\r
144 \r
145                 video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
146 \r
147                 PassCount = 0;\r
148                 int transparentCount = 0;\r
149                 int solidCount = 0;\r
150 \r
151                 // count transparent and solid materials in this scene node\r
152                 const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size();\r
153                 for (u32 i=0; i<numMaterials; ++i)\r
154                 {\r
155                         const video::SMaterial& material = ReadOnlyMaterials ? Mesh->getMeshBuffer(i)->getMaterial() : Materials[i];\r
156 \r
157                         if ( driver->needsTransparentRenderPass(material) )\r
158                                 ++transparentCount;\r
159                         else\r
160                                 ++solidCount;\r
161 \r
162                         if (solidCount && transparentCount)\r
163                                 break;\r
164                 }\r
165 \r
166                 // register according to material types counted\r
167 \r
168                 if (solidCount)\r
169                         SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);\r
170 \r
171                 if (transparentCount)\r
172                         SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);\r
173 \r
174                 ISceneNode::OnRegisterSceneNode();\r
175         }\r
176 }\r
177 \r
178 IMesh * CAnimatedMeshSceneNode::getMeshForCurrentFrame()\r
179 {\r
180         if(Mesh->getMeshType() != EAMT_SKINNED)\r
181         {\r
182                 s32 frameNr = (s32) getFrameNr();\r
183                 s32 frameBlend = (s32) (core::fract ( getFrameNr() ) * 1000.f);\r
184                 return Mesh->getMesh(frameNr, frameBlend, StartFrame, EndFrame);\r
185         }\r
186         else\r
187         {\r
188 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
189                 return 0;\r
190 #else\r
191 \r
192                 // As multiple scene nodes may be sharing the same skinned mesh, we have to\r
193                 // re-animate it every frame to ensure that this node gets the mesh that it needs.\r
194 \r
195                 CSkinnedMesh* skinnedMesh = static_cast<CSkinnedMesh*>(Mesh);\r
196 \r
197                 if (JointMode == EJUOR_CONTROL)//write to mesh\r
198                         skinnedMesh->transferJointsToMesh(JointChildSceneNodes);\r
199                 else\r
200                         skinnedMesh->animateMesh(getFrameNr(), 1.0f);\r
201 \r
202                 // Update the skinned mesh for the current joint transforms.\r
203                 skinnedMesh->skinMesh();\r
204 \r
205                 if (JointMode == EJUOR_READ)//read from mesh\r
206                 {\r
207                         skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes);\r
208 \r
209                         //---slow---\r
210                         for (u32 n=0;n<JointChildSceneNodes.size();++n)\r
211                                 if (JointChildSceneNodes[n]->getParent()==this)\r
212                                 {\r
213                                         JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option\r
214                                 }\r
215                 }\r
216 \r
217                 if(JointMode == EJUOR_CONTROL)\r
218                 {\r
219                         // For meshes other than EJUOR_CONTROL, this is done by calling animateMesh()\r
220                         skinnedMesh->updateBoundingBox();\r
221                 }\r
222 \r
223                 return skinnedMesh;\r
224 #endif\r
225         }\r
226 }\r
227 \r
228 \r
229 //! OnAnimate() is called just before rendering the whole scene.\r
230 void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)\r
231 {\r
232         if (LastTimeMs==0)      // first frame\r
233         {\r
234                 LastTimeMs = timeMs;\r
235         }\r
236 \r
237         // set CurrentFrameNr\r
238         buildFrameNr(timeMs-LastTimeMs);\r
239 \r
240         // update bbox\r
241         if (Mesh)\r
242         {\r
243                 scene::IMesh * mesh = getMeshForCurrentFrame();\r
244 \r
245                 if (mesh)\r
246                         Box = mesh->getBoundingBox();\r
247         }\r
248         LastTimeMs = timeMs;\r
249 \r
250         IAnimatedMeshSceneNode::OnAnimate(timeMs);\r
251 }\r
252 \r
253 \r
254 //! renders the node.\r
255 void CAnimatedMeshSceneNode::render()\r
256 {\r
257         video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
258 \r
259         if (!Mesh || !driver)\r
260                 return;\r
261 \r
262 \r
263         const bool isTransparentPass =\r
264                 SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;\r
265 \r
266         ++PassCount;\r
267 \r
268         scene::IMesh* m = getMeshForCurrentFrame();\r
269 \r
270         if(m)\r
271         {\r
272                 Box = m->getBoundingBox();\r
273         }\r
274         else\r
275         {\r
276                 #ifdef _DEBUG\r
277                         os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING);\r
278                 #endif\r
279                 return;\r
280         }\r
281 \r
282         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
283 \r
284         // for debug purposes only:\r
285 \r
286         bool renderMeshes = true;\r
287         video::SMaterial mat;\r
288         if (DebugDataVisible && PassCount==1)\r
289         {\r
290                 // overwrite half transparency\r
291                 if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY)\r
292                 {\r
293 \r
294                         for (u32 i=0; i<m->getMeshBufferCount(); ++i)\r
295                         {\r
296                                 scene::IMeshBuffer* mb = m->getMeshBuffer(i);\r
297                                 mat = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];\r
298                                 mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;\r
299                                 if (RenderFromIdentity)\r
300                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
301                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
302                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
303 \r
304                                 driver->setMaterial(mat);\r
305                                 driver->drawMeshBuffer(mb);\r
306                         }\r
307                         renderMeshes = false;\r
308                 }\r
309         }\r
310 \r
311         // render original meshes\r
312         if (renderMeshes)\r
313         {\r
314                 for (u32 i=0; i<m->getMeshBufferCount(); ++i)\r
315                 {\r
316                         const bool transparent = driver->needsTransparentRenderPass(Materials[i]);\r
317 \r
318                         // only render transparent buffer if this is the transparent render pass\r
319                         // and solid only in solid pass\r
320                         if (transparent == isTransparentPass)\r
321                         {\r
322                                 scene::IMeshBuffer* mb = m->getMeshBuffer(i);\r
323                                 const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];\r
324                                 if (RenderFromIdentity)\r
325                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
326                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
327                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
328 \r
329                                 driver->setMaterial(material);\r
330                                 driver->drawMeshBuffer(mb);\r
331                         }\r
332                 }\r
333         }\r
334 \r
335         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
336 \r
337         // for debug purposes only:\r
338         if (DebugDataVisible && PassCount==1)\r
339         {\r
340                 video::SMaterial debug_mat;\r
341                 debug_mat.Lighting = false;\r
342                 debug_mat.AntiAliasing=0;\r
343                 driver->setMaterial(debug_mat);\r
344                 // show normals\r
345                 if (DebugDataVisible & scene::EDS_NORMALS)\r
346                 {\r
347                         const f32 debugNormalLength = 1.f;\r
348                         const video::SColor debugNormalColor = video::SColor(255, 34, 221, 221);\r
349                         const u32 count = m->getMeshBufferCount();\r
350 \r
351                         // draw normals\r
352                         for (u32 g=0; g < count; ++g)\r
353                         {\r
354                                 scene::IMeshBuffer* mb = m->getMeshBuffer(g);\r
355                                 if (RenderFromIdentity)\r
356                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
357                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
358                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
359 \r
360                                 driver->drawMeshBufferNormals(mb, debugNormalLength, debugNormalColor);\r
361                         }\r
362                 }\r
363 \r
364                 debug_mat.ZBuffer = video::ECFN_DISABLED;\r
365                 debug_mat.Lighting = false;\r
366                 driver->setMaterial(debug_mat);\r
367 \r
368                 if (DebugDataVisible & scene::EDS_BBOX)\r
369                         driver->draw3DBox(Box, video::SColor(255,255,255,255));\r
370 \r
371                 // show bounding box\r
372                 if (DebugDataVisible & scene::EDS_BBOX_BUFFERS)\r
373                 {\r
374                         for (u32 g=0; g< m->getMeshBufferCount(); ++g)\r
375                         {\r
376                                 const IMeshBuffer* mb = m->getMeshBuffer(g);\r
377 \r
378                                 if (Mesh->getMeshType() == EAMT_SKINNED)\r
379                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
380                                 driver->draw3DBox(mb->getBoundingBox(), video::SColor(255,190,128,128));\r
381                         }\r
382                 }\r
383 \r
384                 // show skeleton\r
385                 if (DebugDataVisible & scene::EDS_SKELETON)\r
386                 {\r
387                         if (Mesh->getMeshType() == EAMT_SKINNED)\r
388                         {\r
389                                 // draw skeleton\r
390 \r
391                                 for (u32 g=0; g < ((ISkinnedMesh*)Mesh)->getAllJoints().size(); ++g)\r
392                                 {\r
393                                         ISkinnedMesh::SJoint *joint=((ISkinnedMesh*)Mesh)->getAllJoints()[g];\r
394 \r
395                                         for (u32 n=0;n<joint->Children.size();++n)\r
396                                         {\r
397                                                 driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),\r
398                                                                 joint->Children[n]->GlobalAnimatedMatrix.getTranslation(),\r
399                                                                 video::SColor(255,51,66,255));\r
400                                         }\r
401                                 }\r
402                         }\r
403                 }\r
404 \r
405                 // show mesh\r
406                 if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY)\r
407                 {\r
408                         debug_mat.Lighting = false;\r
409                         debug_mat.Wireframe = true;\r
410                         debug_mat.ZBuffer = video::ECFN_DISABLED;\r
411                         driver->setMaterial(debug_mat);\r
412 \r
413                         for (u32 g=0; g<m->getMeshBufferCount(); ++g)\r
414                         {\r
415                                 const IMeshBuffer* mb = m->getMeshBuffer(g);\r
416                                 if (RenderFromIdentity)\r
417                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
418                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
419                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
420                                 driver->drawMeshBuffer(mb);\r
421                         }\r
422                 }\r
423         }\r
424 }\r
425 \r
426 \r
427 //! Returns the current start frame number.\r
428 s32 CAnimatedMeshSceneNode::getStartFrame() const\r
429 {\r
430         return StartFrame;\r
431 }\r
432 \r
433 \r
434 //! Returns the current start frame number.\r
435 s32 CAnimatedMeshSceneNode::getEndFrame() const\r
436 {\r
437         return EndFrame;\r
438 }\r
439 \r
440 \r
441 //! sets the frames between the animation is looped.\r
442 //! the default is 0 - MaximalFrameCount of the mesh.\r
443 bool CAnimatedMeshSceneNode::setFrameLoop(s32 begin, s32 end)\r
444 {\r
445         const s32 maxFrameCount = Mesh->getFrameCount() - 1;\r
446         if (end < begin)\r
447         {\r
448                 StartFrame = core::s32_clamp(end, 0, maxFrameCount);\r
449                 EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount);\r
450         }\r
451         else\r
452         {\r
453                 StartFrame = core::s32_clamp(begin, 0, maxFrameCount);\r
454                 EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount);\r
455         }\r
456         if (FramesPerSecond < 0)\r
457                 setCurrentFrame((f32)EndFrame);\r
458         else\r
459                 setCurrentFrame((f32)StartFrame);\r
460 \r
461         return true;\r
462 }\r
463 \r
464 \r
465 //! sets the speed with witch the animation is played\r
466 void CAnimatedMeshSceneNode::setAnimationSpeed(f32 framesPerSecond)\r
467 {\r
468         FramesPerSecond = framesPerSecond * 0.001f;\r
469 }\r
470 \r
471 \r
472 f32 CAnimatedMeshSceneNode::getAnimationSpeed() const\r
473 {\r
474         return FramesPerSecond * 1000.f;\r
475 }\r
476 \r
477 \r
478 //! returns the axis aligned bounding box of this node\r
479 const core::aabbox3d<f32>& CAnimatedMeshSceneNode::getBoundingBox() const\r
480 {\r
481         return Box;\r
482 }\r
483 \r
484 \r
485 //! returns the material based on the zero based index i.\r
486 video::SMaterial& CAnimatedMeshSceneNode::getMaterial(u32 i)\r
487 {\r
488         if (i >= Materials.size())\r
489                 return ISceneNode::getMaterial(i);\r
490 \r
491         return Materials[i];\r
492 }\r
493 \r
494 \r
495 \r
496 //! returns amount of materials used by this scene node.\r
497 u32 CAnimatedMeshSceneNode::getMaterialCount() const\r
498 {\r
499         return Materials.size();\r
500 }\r
501 \r
502 \r
503 //! Returns a pointer to a child node, which has the same transformation as\r
504 //! the corresponding joint, if the mesh in this scene node is a skinned mesh.\r
505 IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(const c8* jointName)\r
506 {\r
507 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
508         os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);\r
509         return 0;\r
510 #else\r
511 \r
512         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
513         {\r
514                 os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);\r
515                 return 0;\r
516         }\r
517 \r
518         checkJoints();\r
519 \r
520         ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;\r
521 \r
522         const s32 number = skinnedMesh->getJointNumber(jointName);\r
523 \r
524         if (number == -1)\r
525         {\r
526                 os::Printer::log("Joint with specified name not found in skinned mesh", jointName, ELL_DEBUG);\r
527                 return 0;\r
528         }\r
529 \r
530         if ((s32)JointChildSceneNodes.size() <= number)\r
531         {\r
532                 os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING);\r
533                 return 0;\r
534         }\r
535 \r
536         return JointChildSceneNodes[number];\r
537 #endif\r
538 }\r
539 \r
540 \r
541 \r
542 //! Returns a pointer to a child node, which has the same transformation as\r
543 //! the corresponding joint, if the mesh in this scene node is a skinned mesh.\r
544 IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(u32 jointID)\r
545 {\r
546 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
547         os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);\r
548         return 0;\r
549 #else\r
550 \r
551         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
552         {\r
553                 os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);\r
554                 return 0;\r
555         }\r
556 \r
557         checkJoints();\r
558 \r
559         if (JointChildSceneNodes.size() <= jointID)\r
560         {\r
561                 os::Printer::log("Joint not loaded into node", ELL_WARNING);\r
562                 return 0;\r
563         }\r
564 \r
565         return JointChildSceneNodes[jointID];\r
566 #endif\r
567 }\r
568 \r
569 //! Gets joint count.\r
570 u32 CAnimatedMeshSceneNode::getJointCount() const\r
571 {\r
572 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
573         return 0;\r
574 #else\r
575 \r
576         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
577                 return 0;\r
578 \r
579         ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;\r
580 \r
581         return skinnedMesh->getJointCount();\r
582 #endif\r
583 }\r
584 \r
585 \r
586 //! Removes a child from this scene node.\r
587 //! Implemented here, to be able to remove the shadow properly, if there is one,\r
588 //! or to remove attached childs.\r
589 bool CAnimatedMeshSceneNode::removeChild(ISceneNode* child)\r
590 {\r
591         if (ISceneNode::removeChild(child))\r
592         {\r
593                 if (JointsUsed) //stop weird bugs caused while changing parents as the joints are being created\r
594                 {\r
595                         for (u32 i=0; i<JointChildSceneNodes.size(); ++i)\r
596                         {\r
597                                 if (JointChildSceneNodes[i] == child)\r
598                                 {\r
599                                         JointChildSceneNodes[i] = 0; //remove link to child\r
600                                         break;\r
601                                 }\r
602                         }\r
603                 }\r
604                 return true;\r
605         }\r
606 \r
607         return false;\r
608 }\r
609 \r
610 \r
611 //! Sets looping mode which is on by default. If set to false,\r
612 //! animations will not be looped.\r
613 void CAnimatedMeshSceneNode::setLoopMode(bool playAnimationLooped)\r
614 {\r
615         Looping = playAnimationLooped;\r
616 }\r
617 \r
618 //! returns the current loop mode\r
619 bool CAnimatedMeshSceneNode::getLoopMode() const\r
620 {\r
621         return Looping;\r
622 }\r
623 \r
624 \r
625 //! Sets a callback interface which will be called if an animation\r
626 //! playback has ended. Set this to 0 to disable the callback again.\r
627 void CAnimatedMeshSceneNode::setAnimationEndCallback(IAnimationEndCallBack* callback)\r
628 {\r
629         if (callback == LoopCallBack)\r
630                 return;\r
631 \r
632         if (LoopCallBack)\r
633                 LoopCallBack->drop();\r
634 \r
635         LoopCallBack = callback;\r
636 \r
637         if (LoopCallBack)\r
638                 LoopCallBack->grab();\r
639 }\r
640 \r
641 \r
642 //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.\r
643 void CAnimatedMeshSceneNode::setReadOnlyMaterials(bool readonly)\r
644 {\r
645         ReadOnlyMaterials = readonly;\r
646 }\r
647 \r
648 \r
649 //! Returns if the scene node should not copy the materials of the mesh but use them in a read only style\r
650 bool CAnimatedMeshSceneNode::isReadOnlyMaterials() const\r
651 {\r
652         return ReadOnlyMaterials;\r
653 }\r
654 \r
655 \r
656 //! Sets a new mesh\r
657 void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh* mesh)\r
658 {\r
659         if (!mesh)\r
660                 return; // won't set null mesh\r
661 \r
662         if (Mesh != mesh)\r
663         {\r
664                 if (Mesh)\r
665                         Mesh->drop();\r
666 \r
667                 Mesh = mesh;\r
668 \r
669                 // grab the mesh (it's non-null!)\r
670                 Mesh->grab();\r
671         }\r
672 \r
673         // get materials and bounding box\r
674         Box = Mesh->getBoundingBox();\r
675 \r
676         IMesh* m = Mesh->getMesh(0,0);\r
677         if (m)\r
678         {\r
679                 Materials.clear();\r
680                 Materials.reallocate(m->getMeshBufferCount());\r
681 \r
682                 for (u32 i=0; i<m->getMeshBufferCount(); ++i)\r
683                 {\r
684                         IMeshBuffer* mb = m->getMeshBuffer(i);\r
685                         if (mb)\r
686                                 Materials.push_back(mb->getMaterial());\r
687                         else\r
688                                 Materials.push_back(video::SMaterial());\r
689                 }\r
690         }\r
691 \r
692         // clean up joint nodes\r
693         if (JointsUsed)\r
694         {\r
695                 JointsUsed=false;\r
696                 checkJoints();\r
697         }\r
698 \r
699         // get start and begin time\r
700         setAnimationSpeed(Mesh->getAnimationSpeed());   // NOTE: This had been commented out (but not removed!) in r3526. Which caused meshloader-values for speed to be ignored unless users specified explicitly. Missing a test-case where this could go wrong so I put the code back in.\r
701         setFrameLoop(0, Mesh->getFrameCount()-1);\r
702 }\r
703 \r
704 \r
705 //! updates the absolute position based on the relative and the parents position\r
706 void CAnimatedMeshSceneNode::updateAbsolutePosition()\r
707 {\r
708         IAnimatedMeshSceneNode::updateAbsolutePosition();\r
709 }\r
710 \r
711 //! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set)\r
712 void CAnimatedMeshSceneNode::setJointMode(E_JOINT_UPDATE_ON_RENDER mode)\r
713 {\r
714         checkJoints();\r
715         JointMode=mode;\r
716 }\r
717 \r
718 //! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2)\r
719 //! you must call animateJoints(), or the mesh will not animate\r
720 void CAnimatedMeshSceneNode::setTransitionTime(f32 time)\r
721 {\r
722         const u32 ttime = (u32)core::floor32(time*1000.0f);\r
723         if (TransitionTime==ttime)\r
724                 return;\r
725         TransitionTime = ttime;\r
726         if (ttime != 0)\r
727                 setJointMode(EJUOR_CONTROL);\r
728         else\r
729                 setJointMode(EJUOR_NONE);\r
730 }\r
731 \r
732 \r
733 //! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)\r
734 void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)\r
735 {\r
736         RenderFromIdentity=enable;\r
737 }\r
738 \r
739 \r
740 //! updates the joint positions of this mesh\r
741 void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)\r
742 {\r
743 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
744         return;\r
745 #else\r
746         if (Mesh && Mesh->getMeshType() == EAMT_SKINNED )\r
747         {\r
748                 checkJoints();\r
749                 const f32 frame = getFrameNr(); //old?\r
750 \r
751                 CSkinnedMesh* skinnedMesh=static_cast<CSkinnedMesh*>(Mesh);\r
752 \r
753                 skinnedMesh->transferOnlyJointsHintsToMesh( JointChildSceneNodes );\r
754                 skinnedMesh->animateMesh(frame, 1.0f);\r
755                 skinnedMesh->recoverJointsFromMesh( JointChildSceneNodes);\r
756 \r
757                 //-----------------------------------------\r
758                 //              Transition\r
759                 //-----------------------------------------\r
760 \r
761                 if (Transiting != 0.f)\r
762                 {\r
763                         // Init additional matrices\r
764                         if (PretransitingSave.size()<JointChildSceneNodes.size())\r
765                         {\r
766                                 for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)\r
767                                         PretransitingSave.push_back(core::matrix4());\r
768                         }\r
769 \r
770                         for (u32 n=0; n<JointChildSceneNodes.size(); ++n)\r
771                         {\r
772                                 //------Position------\r
773 \r
774                                 JointChildSceneNodes[n]->setPosition(\r
775                                                 core::lerp(\r
776                                                         PretransitingSave[n].getTranslation(),\r
777                                                         JointChildSceneNodes[n]->getPosition(),\r
778                                                         TransitingBlend));\r
779 \r
780                                 //------Rotation------\r
781 \r
782                                 //Code is slow, needs to be fixed up\r
783 \r
784                                 const core::quaternion RotationStart(PretransitingSave[n].getRotationDegrees()*core::DEGTORAD);\r
785                                 const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation()*core::DEGTORAD);\r
786 \r
787                                 core::quaternion QRotation;\r
788                                 QRotation.slerp(RotationStart, RotationEnd, TransitingBlend);\r
789 \r
790                                 core::vector3df tmpVector;\r
791                                 QRotation.toEuler(tmpVector);\r
792                                 tmpVector*=core::RADTODEG; //convert from radians back to degrees\r
793                                 JointChildSceneNodes[n]->setRotation( tmpVector );\r
794 \r
795                                 //------Scale------\r
796 \r
797                                 //JointChildSceneNodes[n]->setScale(\r
798                                 //              core::lerp(\r
799                                 //                      PretransitingSave[n].getScale(),\r
800                                 //                      JointChildSceneNodes[n]->getScale(),\r
801                                 //                      TransitingBlend));\r
802                         }\r
803                 }\r
804 \r
805                 if (CalculateAbsolutePositions)\r
806                 {\r
807                         //---slow---\r
808                         for (u32 n=0;n<JointChildSceneNodes.size();++n)\r
809                         {\r
810                                 if (JointChildSceneNodes[n]->getParent()==this)\r
811                                 {\r
812                                         JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option\r
813                                 }\r
814                         }\r
815                 }\r
816         }\r
817 #endif\r
818 }\r
819 \r
820 /*!\r
821 */\r
822 void CAnimatedMeshSceneNode::checkJoints()\r
823 {\r
824 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
825         return;\r
826 #else\r
827 \r
828         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
829                 return;\r
830 \r
831         if (!JointsUsed)\r
832         {\r
833                 for (u32 i=0; i<JointChildSceneNodes.size(); ++i)\r
834                         removeChild(JointChildSceneNodes[i]);\r
835                 JointChildSceneNodes.clear();\r
836 \r
837                 //Create joints for SkinnedMesh\r
838                 ((CSkinnedMesh*)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);\r
839                 ((CSkinnedMesh*)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);\r
840 \r
841                 JointsUsed=true;\r
842                 JointMode=EJUOR_READ;\r
843         }\r
844 #endif\r
845 }\r
846 \r
847 /*!\r
848 */\r
849 void CAnimatedMeshSceneNode::beginTransition()\r
850 {\r
851         if (!JointsUsed)\r
852                 return;\r
853 \r
854         if (TransitionTime != 0)\r
855         {\r
856                 //Check the array is big enough\r
857                 if (PretransitingSave.size()<JointChildSceneNodes.size())\r
858                 {\r
859                         for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)\r
860                                 PretransitingSave.push_back(core::matrix4());\r
861                 }\r
862 \r
863                 //Copy the position of joints\r
864                 for (u32 n=0;n<JointChildSceneNodes.size();++n)\r
865                         PretransitingSave[n]=JointChildSceneNodes[n]->getRelativeTransformation();\r
866 \r
867                 Transiting = core::reciprocal((f32)TransitionTime);\r
868         }\r
869         TransitingBlend = 0.f;\r
870 }\r
871 \r
872 \r
873 /*!\r
874 */\r
875 ISceneNode* CAnimatedMeshSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)\r
876 {\r
877         if (!newParent)\r
878                 newParent = Parent;\r
879         if (!newManager)\r
880                 newManager = SceneManager;\r
881 \r
882         CAnimatedMeshSceneNode* newNode =\r
883                 new CAnimatedMeshSceneNode(Mesh, NULL, newManager, ID, RelativeTranslation,\r
884                                                  RelativeRotation, RelativeScale);\r
885 \r
886         if (newParent)\r
887         {\r
888                 newNode->setParent(newParent);  // not in constructor because virtual overload for updateAbsolutePosition won't be called\r
889                 newNode->drop();\r
890         }\r
891 \r
892         newNode->cloneMembers(this, newManager);\r
893 \r
894         newNode->Materials = Materials;\r
895         newNode->Box = Box;\r
896         newNode->Mesh = Mesh;\r
897         newNode->StartFrame = StartFrame;\r
898         newNode->EndFrame = EndFrame;\r
899         newNode->FramesPerSecond = FramesPerSecond;\r
900         newNode->CurrentFrameNr = CurrentFrameNr;\r
901         newNode->JointMode = JointMode;\r
902         newNode->JointsUsed = JointsUsed;\r
903         newNode->TransitionTime = TransitionTime;\r
904         newNode->Transiting = Transiting;\r
905         newNode->TransitingBlend = TransitingBlend;\r
906         newNode->Looping = Looping;\r
907         newNode->ReadOnlyMaterials = ReadOnlyMaterials;\r
908         newNode->LoopCallBack = LoopCallBack;\r
909         if (newNode->LoopCallBack)\r
910                 newNode->LoopCallBack->grab();\r
911         newNode->PassCount = PassCount;\r
912         newNode->JointChildSceneNodes = JointChildSceneNodes;\r
913         newNode->PretransitingSave = PretransitingSave;\r
914         newNode->RenderFromIdentity = RenderFromIdentity;\r
915 \r
916         return newNode;\r
917 }\r
918 \r
919 \r
920 } // end namespace scene\r
921 } // end namespace irr\r