]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CAnimatedMeshSceneNode.cpp
9d92e03875c54c5eb31f7988b7e2370a64c2948f
[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         LastTimeMs = timeMs;\r
240 \r
241         IAnimatedMeshSceneNode::OnAnimate(timeMs);\r
242 }\r
243 \r
244 \r
245 //! renders the node.\r
246 void CAnimatedMeshSceneNode::render()\r
247 {\r
248         video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
249 \r
250         if (!Mesh || !driver)\r
251                 return;\r
252 \r
253 \r
254         const bool isTransparentPass =\r
255                 SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;\r
256 \r
257         ++PassCount;\r
258 \r
259         scene::IMesh* m = getMeshForCurrentFrame();\r
260 \r
261         if(m)\r
262         {\r
263                 Box = m->getBoundingBox();\r
264         }\r
265         else\r
266         {\r
267                 #ifdef _DEBUG\r
268                         os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING);\r
269                 #endif\r
270                 return;\r
271         }\r
272 \r
273         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
274 \r
275         // for debug purposes only:\r
276 \r
277         bool renderMeshes = true;\r
278         video::SMaterial mat;\r
279         if (DebugDataVisible && PassCount==1)\r
280         {\r
281                 // overwrite half transparency\r
282                 if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY)\r
283                 {\r
284 \r
285                         for (u32 i=0; i<m->getMeshBufferCount(); ++i)\r
286                         {\r
287                                 scene::IMeshBuffer* mb = m->getMeshBuffer(i);\r
288                                 mat = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];\r
289                                 mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;\r
290                                 if (RenderFromIdentity)\r
291                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
292                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
293                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
294 \r
295                                 driver->setMaterial(mat);\r
296                                 driver->drawMeshBuffer(mb);\r
297                         }\r
298                         renderMeshes = false;\r
299                 }\r
300         }\r
301 \r
302         // render original meshes\r
303         if (renderMeshes)\r
304         {\r
305                 for (u32 i=0; i<m->getMeshBufferCount(); ++i)\r
306                 {\r
307                         const bool transparent = driver->needsTransparentRenderPass(Materials[i]);\r
308 \r
309                         // only render transparent buffer if this is the transparent render pass\r
310                         // and solid only in solid pass\r
311                         if (transparent == isTransparentPass)\r
312                         {\r
313                                 scene::IMeshBuffer* mb = m->getMeshBuffer(i);\r
314                                 const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];\r
315                                 if (RenderFromIdentity)\r
316                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
317                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
318                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
319 \r
320                                 driver->setMaterial(material);\r
321                                 driver->drawMeshBuffer(mb);\r
322                         }\r
323                 }\r
324         }\r
325 \r
326         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
327 \r
328         // for debug purposes only:\r
329         if (DebugDataVisible && PassCount==1)\r
330         {\r
331                 video::SMaterial debug_mat;\r
332                 debug_mat.Lighting = false;\r
333                 debug_mat.AntiAliasing=0;\r
334                 driver->setMaterial(debug_mat);\r
335                 // show normals\r
336                 if (DebugDataVisible & scene::EDS_NORMALS)\r
337                 {\r
338                         const f32 debugNormalLength = 1.f;\r
339                         const video::SColor debugNormalColor = video::SColor(255, 34, 221, 221);\r
340                         const u32 count = m->getMeshBufferCount();\r
341 \r
342                         // draw normals\r
343                         for (u32 g=0; g < count; ++g)\r
344                         {\r
345                                 scene::IMeshBuffer* mb = m->getMeshBuffer(g);\r
346                                 if (RenderFromIdentity)\r
347                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
348                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
349                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
350 \r
351                                 driver->drawMeshBufferNormals(mb, debugNormalLength, debugNormalColor);\r
352                         }\r
353                 }\r
354 \r
355                 debug_mat.ZBuffer = video::ECFN_DISABLED;\r
356                 debug_mat.Lighting = false;\r
357                 driver->setMaterial(debug_mat);\r
358 \r
359                 if (DebugDataVisible & scene::EDS_BBOX)\r
360                         driver->draw3DBox(Box, video::SColor(255,255,255,255));\r
361 \r
362                 // show bounding box\r
363                 if (DebugDataVisible & scene::EDS_BBOX_BUFFERS)\r
364                 {\r
365                         for (u32 g=0; g< m->getMeshBufferCount(); ++g)\r
366                         {\r
367                                 const IMeshBuffer* mb = m->getMeshBuffer(g);\r
368 \r
369                                 if (Mesh->getMeshType() == EAMT_SKINNED)\r
370                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
371                                 driver->draw3DBox(mb->getBoundingBox(), video::SColor(255,190,128,128));\r
372                         }\r
373                 }\r
374 \r
375                 // show skeleton\r
376                 if (DebugDataVisible & scene::EDS_SKELETON)\r
377                 {\r
378                         if (Mesh->getMeshType() == EAMT_SKINNED)\r
379                         {\r
380                                 // draw skeleton\r
381 \r
382                                 for (u32 g=0; g < ((ISkinnedMesh*)Mesh)->getAllJoints().size(); ++g)\r
383                                 {\r
384                                         ISkinnedMesh::SJoint *joint=((ISkinnedMesh*)Mesh)->getAllJoints()[g];\r
385 \r
386                                         for (u32 n=0;n<joint->Children.size();++n)\r
387                                         {\r
388                                                 driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),\r
389                                                                 joint->Children[n]->GlobalAnimatedMatrix.getTranslation(),\r
390                                                                 video::SColor(255,51,66,255));\r
391                                         }\r
392                                 }\r
393                         }\r
394                 }\r
395 \r
396                 // show mesh\r
397                 if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY)\r
398                 {\r
399                         debug_mat.Lighting = false;\r
400                         debug_mat.Wireframe = true;\r
401                         debug_mat.ZBuffer = video::ECFN_DISABLED;\r
402                         driver->setMaterial(debug_mat);\r
403 \r
404                         for (u32 g=0; g<m->getMeshBufferCount(); ++g)\r
405                         {\r
406                                 const IMeshBuffer* mb = m->getMeshBuffer(g);\r
407                                 if (RenderFromIdentity)\r
408                                         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );\r
409                                 else if (Mesh->getMeshType() == EAMT_SKINNED)\r
410                                         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);\r
411                                 driver->drawMeshBuffer(mb);\r
412                         }\r
413                 }\r
414         }\r
415 }\r
416 \r
417 \r
418 //! Returns the current start frame number.\r
419 s32 CAnimatedMeshSceneNode::getStartFrame() const\r
420 {\r
421         return StartFrame;\r
422 }\r
423 \r
424 \r
425 //! Returns the current start frame number.\r
426 s32 CAnimatedMeshSceneNode::getEndFrame() const\r
427 {\r
428         return EndFrame;\r
429 }\r
430 \r
431 \r
432 //! sets the frames between the animation is looped.\r
433 //! the default is 0 - MaximalFrameCount of the mesh.\r
434 bool CAnimatedMeshSceneNode::setFrameLoop(s32 begin, s32 end)\r
435 {\r
436         const s32 maxFrameCount = Mesh->getFrameCount() - 1;\r
437         if (end < begin)\r
438         {\r
439                 StartFrame = core::s32_clamp(end, 0, maxFrameCount);\r
440                 EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount);\r
441         }\r
442         else\r
443         {\r
444                 StartFrame = core::s32_clamp(begin, 0, maxFrameCount);\r
445                 EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount);\r
446         }\r
447         if (FramesPerSecond < 0)\r
448                 setCurrentFrame((f32)EndFrame);\r
449         else\r
450                 setCurrentFrame((f32)StartFrame);\r
451 \r
452         return true;\r
453 }\r
454 \r
455 \r
456 //! sets the speed with witch the animation is played\r
457 void CAnimatedMeshSceneNode::setAnimationSpeed(f32 framesPerSecond)\r
458 {\r
459         FramesPerSecond = framesPerSecond * 0.001f;\r
460 }\r
461 \r
462 \r
463 f32 CAnimatedMeshSceneNode::getAnimationSpeed() const\r
464 {\r
465         return FramesPerSecond * 1000.f;\r
466 }\r
467 \r
468 \r
469 //! returns the axis aligned bounding box of this node\r
470 const core::aabbox3d<f32>& CAnimatedMeshSceneNode::getBoundingBox() const\r
471 {\r
472         return Box;\r
473 }\r
474 \r
475 \r
476 //! returns the material based on the zero based index i.\r
477 video::SMaterial& CAnimatedMeshSceneNode::getMaterial(u32 i)\r
478 {\r
479         if (i >= Materials.size())\r
480                 return ISceneNode::getMaterial(i);\r
481 \r
482         return Materials[i];\r
483 }\r
484 \r
485 \r
486 \r
487 //! returns amount of materials used by this scene node.\r
488 u32 CAnimatedMeshSceneNode::getMaterialCount() const\r
489 {\r
490         return Materials.size();\r
491 }\r
492 \r
493 \r
494 //! Returns a pointer to a child node, which has the same transformation as\r
495 //! the corresponding joint, if the mesh in this scene node is a skinned mesh.\r
496 IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(const c8* jointName)\r
497 {\r
498 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
499         os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);\r
500         return 0;\r
501 #else\r
502 \r
503         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
504         {\r
505                 os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);\r
506                 return 0;\r
507         }\r
508 \r
509         checkJoints();\r
510 \r
511         ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;\r
512 \r
513         const s32 number = skinnedMesh->getJointNumber(jointName);\r
514 \r
515         if (number == -1)\r
516         {\r
517                 os::Printer::log("Joint with specified name not found in skinned mesh", jointName, ELL_DEBUG);\r
518                 return 0;\r
519         }\r
520 \r
521         if ((s32)JointChildSceneNodes.size() <= number)\r
522         {\r
523                 os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING);\r
524                 return 0;\r
525         }\r
526 \r
527         return JointChildSceneNodes[number];\r
528 #endif\r
529 }\r
530 \r
531 \r
532 \r
533 //! Returns a pointer to a child node, which has the same transformation as\r
534 //! the corresponding joint, if the mesh in this scene node is a skinned mesh.\r
535 IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(u32 jointID)\r
536 {\r
537 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
538         os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);\r
539         return 0;\r
540 #else\r
541 \r
542         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
543         {\r
544                 os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);\r
545                 return 0;\r
546         }\r
547 \r
548         checkJoints();\r
549 \r
550         if (JointChildSceneNodes.size() <= jointID)\r
551         {\r
552                 os::Printer::log("Joint not loaded into node", ELL_WARNING);\r
553                 return 0;\r
554         }\r
555 \r
556         return JointChildSceneNodes[jointID];\r
557 #endif\r
558 }\r
559 \r
560 //! Gets joint count.\r
561 u32 CAnimatedMeshSceneNode::getJointCount() const\r
562 {\r
563 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
564         return 0;\r
565 #else\r
566 \r
567         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
568                 return 0;\r
569 \r
570         ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;\r
571 \r
572         return skinnedMesh->getJointCount();\r
573 #endif\r
574 }\r
575 \r
576 \r
577 //! Removes a child from this scene node.\r
578 //! Implemented here, to be able to remove the shadow properly, if there is one,\r
579 //! or to remove attached childs.\r
580 bool CAnimatedMeshSceneNode::removeChild(ISceneNode* child)\r
581 {\r
582         if (ISceneNode::removeChild(child))\r
583         {\r
584                 if (JointsUsed) //stop weird bugs caused while changing parents as the joints are being created\r
585                 {\r
586                         for (u32 i=0; i<JointChildSceneNodes.size(); ++i)\r
587                         {\r
588                                 if (JointChildSceneNodes[i] == child)\r
589                                 {\r
590                                         JointChildSceneNodes[i] = 0; //remove link to child\r
591                                         break;\r
592                                 }\r
593                         }\r
594                 }\r
595                 return true;\r
596         }\r
597 \r
598         return false;\r
599 }\r
600 \r
601 \r
602 //! Sets looping mode which is on by default. If set to false,\r
603 //! animations will not be looped.\r
604 void CAnimatedMeshSceneNode::setLoopMode(bool playAnimationLooped)\r
605 {\r
606         Looping = playAnimationLooped;\r
607 }\r
608 \r
609 //! returns the current loop mode\r
610 bool CAnimatedMeshSceneNode::getLoopMode() const\r
611 {\r
612         return Looping;\r
613 }\r
614 \r
615 \r
616 //! Sets a callback interface which will be called if an animation\r
617 //! playback has ended. Set this to 0 to disable the callback again.\r
618 void CAnimatedMeshSceneNode::setAnimationEndCallback(IAnimationEndCallBack* callback)\r
619 {\r
620         if (callback == LoopCallBack)\r
621                 return;\r
622 \r
623         if (LoopCallBack)\r
624                 LoopCallBack->drop();\r
625 \r
626         LoopCallBack = callback;\r
627 \r
628         if (LoopCallBack)\r
629                 LoopCallBack->grab();\r
630 }\r
631 \r
632 \r
633 //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.\r
634 void CAnimatedMeshSceneNode::setReadOnlyMaterials(bool readonly)\r
635 {\r
636         ReadOnlyMaterials = readonly;\r
637 }\r
638 \r
639 \r
640 //! Returns if the scene node should not copy the materials of the mesh but use them in a read only style\r
641 bool CAnimatedMeshSceneNode::isReadOnlyMaterials() const\r
642 {\r
643         return ReadOnlyMaterials;\r
644 }\r
645 \r
646 \r
647 //! Sets a new mesh\r
648 void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh* mesh)\r
649 {\r
650         if (!mesh)\r
651                 return; // won't set null mesh\r
652 \r
653         if (Mesh != mesh)\r
654         {\r
655                 if (Mesh)\r
656                         Mesh->drop();\r
657 \r
658                 Mesh = mesh;\r
659 \r
660                 // grab the mesh (it's non-null!)\r
661                 Mesh->grab();\r
662         }\r
663 \r
664         // get materials and bounding box\r
665         Box = Mesh->getBoundingBox();\r
666 \r
667         IMesh* m = Mesh->getMesh(0,0);\r
668         if (m)\r
669         {\r
670                 Materials.clear();\r
671                 Materials.reallocate(m->getMeshBufferCount());\r
672 \r
673                 for (u32 i=0; i<m->getMeshBufferCount(); ++i)\r
674                 {\r
675                         IMeshBuffer* mb = m->getMeshBuffer(i);\r
676                         if (mb)\r
677                                 Materials.push_back(mb->getMaterial());\r
678                         else\r
679                                 Materials.push_back(video::SMaterial());\r
680                 }\r
681         }\r
682 \r
683         // clean up joint nodes\r
684         if (JointsUsed)\r
685         {\r
686                 JointsUsed=false;\r
687                 checkJoints();\r
688         }\r
689 \r
690         // get start and begin time\r
691         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
692         setFrameLoop(0, Mesh->getFrameCount()-1);\r
693 }\r
694 \r
695 \r
696 //! updates the absolute position based on the relative and the parents position\r
697 void CAnimatedMeshSceneNode::updateAbsolutePosition()\r
698 {\r
699         IAnimatedMeshSceneNode::updateAbsolutePosition();\r
700 }\r
701 \r
702 //! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set)\r
703 void CAnimatedMeshSceneNode::setJointMode(E_JOINT_UPDATE_ON_RENDER mode)\r
704 {\r
705         checkJoints();\r
706         JointMode=mode;\r
707 }\r
708 \r
709 //! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2)\r
710 //! you must call animateJoints(), or the mesh will not animate\r
711 void CAnimatedMeshSceneNode::setTransitionTime(f32 time)\r
712 {\r
713         const u32 ttime = (u32)core::floor32(time*1000.0f);\r
714         if (TransitionTime==ttime)\r
715                 return;\r
716         TransitionTime = ttime;\r
717         if (ttime != 0)\r
718                 setJointMode(EJUOR_CONTROL);\r
719         else\r
720                 setJointMode(EJUOR_NONE);\r
721 }\r
722 \r
723 \r
724 //! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)\r
725 void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)\r
726 {\r
727         RenderFromIdentity=enable;\r
728 }\r
729 \r
730 \r
731 //! updates the joint positions of this mesh\r
732 void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)\r
733 {\r
734 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
735         return;\r
736 #else\r
737         if (Mesh && Mesh->getMeshType() == EAMT_SKINNED )\r
738         {\r
739                 checkJoints();\r
740                 const f32 frame = getFrameNr(); //old?\r
741 \r
742                 CSkinnedMesh* skinnedMesh=static_cast<CSkinnedMesh*>(Mesh);\r
743 \r
744                 skinnedMesh->transferOnlyJointsHintsToMesh( JointChildSceneNodes );\r
745                 skinnedMesh->animateMesh(frame, 1.0f);\r
746                 skinnedMesh->recoverJointsFromMesh( JointChildSceneNodes);\r
747 \r
748                 //-----------------------------------------\r
749                 //              Transition\r
750                 //-----------------------------------------\r
751 \r
752                 if (Transiting != 0.f)\r
753                 {\r
754                         // Init additional matrices\r
755                         if (PretransitingSave.size()<JointChildSceneNodes.size())\r
756                         {\r
757                                 for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)\r
758                                         PretransitingSave.push_back(core::matrix4());\r
759                         }\r
760 \r
761                         for (u32 n=0; n<JointChildSceneNodes.size(); ++n)\r
762                         {\r
763                                 //------Position------\r
764 \r
765                                 JointChildSceneNodes[n]->setPosition(\r
766                                                 core::lerp(\r
767                                                         PretransitingSave[n].getTranslation(),\r
768                                                         JointChildSceneNodes[n]->getPosition(),\r
769                                                         TransitingBlend));\r
770 \r
771                                 //------Rotation------\r
772 \r
773                                 //Code is slow, needs to be fixed up\r
774 \r
775                                 const core::quaternion RotationStart(PretransitingSave[n].getRotationDegrees()*core::DEGTORAD);\r
776                                 const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation()*core::DEGTORAD);\r
777 \r
778                                 core::quaternion QRotation;\r
779                                 QRotation.slerp(RotationStart, RotationEnd, TransitingBlend);\r
780 \r
781                                 core::vector3df tmpVector;\r
782                                 QRotation.toEuler(tmpVector);\r
783                                 tmpVector*=core::RADTODEG; //convert from radians back to degrees\r
784                                 JointChildSceneNodes[n]->setRotation( tmpVector );\r
785 \r
786                                 //------Scale------\r
787 \r
788                                 //JointChildSceneNodes[n]->setScale(\r
789                                 //              core::lerp(\r
790                                 //                      PretransitingSave[n].getScale(),\r
791                                 //                      JointChildSceneNodes[n]->getScale(),\r
792                                 //                      TransitingBlend));\r
793                         }\r
794                 }\r
795 \r
796                 if (CalculateAbsolutePositions)\r
797                 {\r
798                         //---slow---\r
799                         for (u32 n=0;n<JointChildSceneNodes.size();++n)\r
800                         {\r
801                                 if (JointChildSceneNodes[n]->getParent()==this)\r
802                                 {\r
803                                         JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option\r
804                                 }\r
805                         }\r
806                 }\r
807         }\r
808 #endif\r
809 }\r
810 \r
811 /*!\r
812 */\r
813 void CAnimatedMeshSceneNode::checkJoints()\r
814 {\r
815 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
816         return;\r
817 #else\r
818 \r
819         if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)\r
820                 return;\r
821 \r
822         if (!JointsUsed)\r
823         {\r
824                 for (u32 i=0; i<JointChildSceneNodes.size(); ++i)\r
825                         removeChild(JointChildSceneNodes[i]);\r
826                 JointChildSceneNodes.clear();\r
827 \r
828                 //Create joints for SkinnedMesh\r
829                 ((CSkinnedMesh*)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);\r
830                 ((CSkinnedMesh*)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);\r
831 \r
832                 JointsUsed=true;\r
833                 JointMode=EJUOR_READ;\r
834         }\r
835 #endif\r
836 }\r
837 \r
838 /*!\r
839 */\r
840 void CAnimatedMeshSceneNode::beginTransition()\r
841 {\r
842         if (!JointsUsed)\r
843                 return;\r
844 \r
845         if (TransitionTime != 0)\r
846         {\r
847                 //Check the array is big enough\r
848                 if (PretransitingSave.size()<JointChildSceneNodes.size())\r
849                 {\r
850                         for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)\r
851                                 PretransitingSave.push_back(core::matrix4());\r
852                 }\r
853 \r
854                 //Copy the position of joints\r
855                 for (u32 n=0;n<JointChildSceneNodes.size();++n)\r
856                         PretransitingSave[n]=JointChildSceneNodes[n]->getRelativeTransformation();\r
857 \r
858                 Transiting = core::reciprocal((f32)TransitionTime);\r
859         }\r
860         TransitingBlend = 0.f;\r
861 }\r
862 \r
863 \r
864 /*!\r
865 */\r
866 ISceneNode* CAnimatedMeshSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)\r
867 {\r
868         if (!newParent)\r
869                 newParent = Parent;\r
870         if (!newManager)\r
871                 newManager = SceneManager;\r
872 \r
873         CAnimatedMeshSceneNode* newNode =\r
874                 new CAnimatedMeshSceneNode(Mesh, NULL, newManager, ID, RelativeTranslation,\r
875                                                  RelativeRotation, RelativeScale);\r
876 \r
877         if (newParent)\r
878         {\r
879                 newNode->setParent(newParent);  // not in constructor because virtual overload for updateAbsolutePosition won't be called\r
880                 newNode->drop();\r
881         }\r
882 \r
883         newNode->cloneMembers(this, newManager);\r
884 \r
885         newNode->Materials = Materials;\r
886         newNode->Box = Box;\r
887         newNode->Mesh = Mesh;\r
888         newNode->StartFrame = StartFrame;\r
889         newNode->EndFrame = EndFrame;\r
890         newNode->FramesPerSecond = FramesPerSecond;\r
891         newNode->CurrentFrameNr = CurrentFrameNr;\r
892         newNode->JointMode = JointMode;\r
893         newNode->JointsUsed = JointsUsed;\r
894         newNode->TransitionTime = TransitionTime;\r
895         newNode->Transiting = Transiting;\r
896         newNode->TransitingBlend = TransitingBlend;\r
897         newNode->Looping = Looping;\r
898         newNode->ReadOnlyMaterials = ReadOnlyMaterials;\r
899         newNode->LoopCallBack = LoopCallBack;\r
900         if (newNode->LoopCallBack)\r
901                 newNode->LoopCallBack->grab();\r
902         newNode->PassCount = PassCount;\r
903         newNode->JointChildSceneNodes = JointChildSceneNodes;\r
904         newNode->PretransitingSave = PretransitingSave;\r
905         newNode->RenderFromIdentity = RenderFromIdentity;\r
906 \r
907         return newNode;\r
908 }\r
909 \r
910 \r
911 } // end namespace scene\r
912 } // end namespace irr\r