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