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