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
5 #include "CAnimatedMeshSceneNode.h"
\r
6 #include "IVideoDriver.h"
\r
7 #include "ISceneManager.h"
\r
8 #include "S3DVertex.h"
\r
10 #ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
\r
11 #include "CShadowVolumeSceneNode.h"
\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
21 #include "IMeshCache.h"
\r
22 #include "IAnimatedMesh.h"
\r
23 #include "quaternion.h"
\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
47 setDebugName("CAnimatedMeshSceneNode");
\r
55 CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode()
\r
67 LoopCallBack->drop();
\r
71 //! Sets the current frame. From now on the animation is played from this frame.
\r
72 void CAnimatedMeshSceneNode::setCurrentFrame(f32 frame)
\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
77 beginTransition(); //transit to this frame if enabled
\r
81 //! Returns the currently displayed frame number.
\r
82 f32 CAnimatedMeshSceneNode::getFrameNr() const
\r
84 return CurrentFrameNr;
\r
88 //! Get CurrentFrameNr and update transiting settings
\r
89 void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
\r
91 if (Transiting!=0.f)
\r
93 TransitingBlend += (f32)(timeMs) * Transiting;
\r
94 if (TransitingBlend > 1.f)
\r
97 TransitingBlend=0.f;
\r
101 if (StartFrame==EndFrame)
\r
103 CurrentFrameNr = (f32)StartFrame; //Support for non animated meshes
\r
107 // play animation looped
\r
108 CurrentFrameNr += timeMs * FramesPerSecond;
\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
114 if (CurrentFrameNr > EndFrame)
\r
115 CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, (f32)(EndFrame-StartFrame));
\r
117 else //backwards...
\r
119 if (CurrentFrameNr < StartFrame)
\r
120 CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, (f32)(EndFrame-StartFrame));
\r
125 // play animation non looped
\r
127 CurrentFrameNr += timeMs * FramesPerSecond;
\r
128 if (FramesPerSecond > 0.f) //forwards...
\r
130 if (CurrentFrameNr > (f32)EndFrame)
\r
132 CurrentFrameNr = (f32)EndFrame;
\r
134 LoopCallBack->OnAnimationEnd(this);
\r
137 else //backwards...
\r
139 if (CurrentFrameNr < (f32)StartFrame)
\r
141 CurrentFrameNr = (f32)StartFrame;
\r
143 LoopCallBack->OnAnimationEnd(this);
\r
150 void CAnimatedMeshSceneNode::OnRegisterSceneNode()
\r
152 if (IsVisible && Mesh)
\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
159 video::IVideoDriver* driver = SceneManager->getVideoDriver();
\r
162 int transparentCount = 0;
\r
163 int solidCount = 0;
\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
169 const video::SMaterial& material = ReadOnlyMaterials ? Mesh->getMeshBuffer(i)->getMaterial() : Materials[i];
\r
171 if ( driver->needsTransparentRenderPass(material) )
\r
172 ++transparentCount;
\r
176 if (solidCount && transparentCount)
\r
180 // register according to material types counted
\r
183 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
\r
185 if (transparentCount)
\r
186 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
\r
188 ISceneNode::OnRegisterSceneNode();
\r
192 IMesh * CAnimatedMeshSceneNode::getMeshForCurrentFrame()
\r
194 if(Mesh->getMeshType() != EAMT_SKINNED)
\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
202 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
\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
209 CSkinnedMesh* skinnedMesh = reinterpret_cast<CSkinnedMesh*>(Mesh);
\r
211 if (JointMode == EJUOR_CONTROL)//write to mesh
\r
212 skinnedMesh->transferJointsToMesh(JointChildSceneNodes);
\r
214 skinnedMesh->animateMesh(getFrameNr(), 1.0f);
\r
216 // Update the skinned mesh for the current joint transforms.
\r
217 skinnedMesh->skinMesh();
\r
219 if (JointMode == EJUOR_READ)//read from mesh
\r
221 skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes);
\r
224 for (u32 n=0;n<JointChildSceneNodes.size();++n)
\r
225 if (JointChildSceneNodes[n]->getParent()==this)
\r
227 JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option
\r
231 if(JointMode == EJUOR_CONTROL)
\r
233 // For meshes other than EJUOR_CONTROL, this is done by calling animateMesh()
\r
234 skinnedMesh->updateBoundingBox();
\r
237 return skinnedMesh;
\r
243 //! OnAnimate() is called just before rendering the whole scene.
\r
244 void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)
\r
246 if (LastTimeMs==0) // first frame
\r
248 LastTimeMs = timeMs;
\r
251 // set CurrentFrameNr
\r
252 buildFrameNr(timeMs-LastTimeMs);
\r
257 scene::IMesh * mesh = getMeshForCurrentFrame();
\r
260 Box = mesh->getBoundingBox();
\r
262 LastTimeMs = timeMs;
\r
264 IAnimatedMeshSceneNode::OnAnimate(timeMs);
\r
268 //! renders the node.
\r
269 void CAnimatedMeshSceneNode::render()
\r
271 video::IVideoDriver* driver = SceneManager->getVideoDriver();
\r
273 if (!Mesh || !driver)
\r
277 const bool isTransparentPass =
\r
278 SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
\r
282 scene::IMesh* m = getMeshForCurrentFrame();
\r
286 Box = m->getBoundingBox();
\r
291 os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING);
\r
295 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
\r
297 if (Shadow && PassCount==1)
\r
298 Shadow->updateShadowVolumes();
\r
300 // for debug purposes only:
\r
302 bool renderMeshes = true;
\r
303 video::SMaterial mat;
\r
304 if (DebugDataVisible && PassCount==1)
\r
306 // overwrite half transparency
\r
307 if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY)
\r
310 for (u32 i=0; i<m->getMeshBufferCount(); ++i)
\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
320 driver->setMaterial(mat);
\r
321 driver->drawMeshBuffer(mb);
\r
323 renderMeshes = false;
\r
327 // render original meshes
\r
330 for (u32 i=0; i<m->getMeshBufferCount(); ++i)
\r
332 const bool transparent = driver->needsTransparentRenderPass(Materials[i]);
\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
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
345 driver->setMaterial(material);
\r
346 driver->drawMeshBuffer(mb);
\r
351 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
\r
353 // for debug purposes only:
\r
354 if (DebugDataVisible && PassCount==1)
\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
361 if (DebugDataVisible & scene::EDS_NORMALS)
\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
368 for (u32 g=0; g < count; ++g)
\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
376 driver->drawMeshBufferNormals(mb, debugNormalLength, debugNormalColor);
\r
380 debug_mat.ZBuffer = video::ECFN_DISABLED;
\r
381 debug_mat.Lighting = false;
\r
382 driver->setMaterial(debug_mat);
\r
384 if (DebugDataVisible & scene::EDS_BBOX)
\r
385 driver->draw3DBox(Box, video::SColor(255,255,255,255));
\r
387 // show bounding box
\r
388 if (DebugDataVisible & scene::EDS_BBOX_BUFFERS)
\r
390 for (u32 g=0; g< m->getMeshBufferCount(); ++g)
\r
392 const IMeshBuffer* mb = m->getMeshBuffer(g);
\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
401 if (DebugDataVisible & scene::EDS_SKELETON)
\r
403 if (Mesh->getMeshType() == EAMT_SKINNED)
\r
407 for (u32 g=0; g < ((ISkinnedMesh*)Mesh)->getAllJoints().size(); ++g)
\r
409 ISkinnedMesh::SJoint *joint=((ISkinnedMesh*)Mesh)->getAllJoints()[g];
\r
411 for (u32 n=0;n<joint->Children.size();++n)
\r
413 driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),
\r
414 joint->Children[n]->GlobalAnimatedMatrix.getTranslation(),
\r
415 video::SColor(255,51,66,255));
\r
420 // show tag for quake3 models
\r
421 if (Mesh->getMeshType() == EAMT_MD3)
\r
423 IAnimatedMesh * arrow =
\r
424 SceneManager->addArrowMesh (
\r
426 0xFF0000FF, 0xFF000088,
\r
427 4, 8, 5.f, 4.f, 0.5f,
\r
431 arrow = SceneManager->getMesh ( "__tag_show" );
\r
433 IMesh *arrowMesh = arrow->getMesh(0);
\r
435 core::matrix4 matr;
\r
437 SMD3QuaternionTagList *taglist = ((IAnimatedMeshMD3*)Mesh)->getTagList(
\r
438 (s32)getFrameNr(), 255,
\r
439 getStartFrame(), getEndFrame());
\r
442 for ( u32 ts = 0; ts != taglist->size(); ++ts )
\r
444 (*taglist)[ts].setto(matr);
\r
446 driver->setTransform(video::ETS_WORLD, matr );
\r
448 for ( u32 a = 0; a != arrowMesh->getMeshBufferCount(); ++a )
\r
449 driver->drawMeshBuffer(arrowMesh->getMeshBuffer(a));
\r
456 if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY)
\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
463 for (u32 g=0; g<m->getMeshBufferCount(); ++g)
\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
477 //! Returns the current start frame number.
\r
478 s32 CAnimatedMeshSceneNode::getStartFrame() const
\r
484 //! Returns the current start frame number.
\r
485 s32 CAnimatedMeshSceneNode::getEndFrame() const
\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
495 const s32 maxFrameCount = Mesh->getFrameCount() - 1;
\r
498 StartFrame = core::s32_clamp(end, 0, maxFrameCount);
\r
499 EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount);
\r
503 StartFrame = core::s32_clamp(begin, 0, maxFrameCount);
\r
504 EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount);
\r
506 if (FramesPerSecond < 0)
\r
507 setCurrentFrame((f32)EndFrame);
\r
509 setCurrentFrame((f32)StartFrame);
\r
515 //! sets the speed with witch the animation is played
\r
516 void CAnimatedMeshSceneNode::setAnimationSpeed(f32 framesPerSecond)
\r
518 FramesPerSecond = framesPerSecond * 0.001f;
\r
522 f32 CAnimatedMeshSceneNode::getAnimationSpeed() const
\r
524 return FramesPerSecond * 1000.f;
\r
528 //! returns the axis aligned bounding box of this node
\r
529 const core::aabbox3d<f32>& CAnimatedMeshSceneNode::getBoundingBox() const
\r
535 //! returns the material based on the zero based index i.
\r
536 video::SMaterial& CAnimatedMeshSceneNode::getMaterial(u32 i)
\r
538 if (i >= Materials.size())
\r
539 return ISceneNode::getMaterial(i);
\r
541 return Materials[i];
\r
546 //! returns amount of materials used by this scene node.
\r
547 u32 CAnimatedMeshSceneNode::getMaterialCount() const
\r
549 return Materials.size();
\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
558 #ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
\r
559 if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))
\r
563 shadowMesh = Mesh; // if null is given, use the mesh of node
\r
568 Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id, zfailmethod, infinity);
\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
579 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
\r
580 os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);
\r
584 if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
\r
586 os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);
\r
592 ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;
\r
594 const s32 number = skinnedMesh->getJointNumber(jointName);
\r
598 os::Printer::log("Joint with specified name not found in skinned mesh", jointName, ELL_DEBUG);
\r
602 if ((s32)JointChildSceneNodes.size() <= number)
\r
604 os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING);
\r
608 return JointChildSceneNodes[number];
\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
618 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
\r
619 os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);
\r
623 if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
\r
625 os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);
\r
631 if (JointChildSceneNodes.size() <= jointID)
\r
633 os::Printer::log("Joint not loaded into node", ELL_WARNING);
\r
637 return JointChildSceneNodes[jointID];
\r
641 //! Gets joint count.
\r
642 u32 CAnimatedMeshSceneNode::getJointCount() const
\r
644 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
\r
648 if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
\r
651 ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;
\r
653 return skinnedMesh->getJointCount();
\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
663 if (child && Shadow == child)
\r
669 if (ISceneNode::removeChild(child))
\r
671 if (JointsUsed) //stop weird bugs caused while changing parents as the joints are being created
\r
673 for (u32 i=0; i<JointChildSceneNodes.size(); ++i)
\r
675 if (JointChildSceneNodes[i] == child)
\r
677 JointChildSceneNodes[i] = 0; //remove link to child
\r
689 //! Starts a MD2 animation.
\r
690 bool CAnimatedMeshSceneNode::setMD2Animation(EMD2_ANIMATION_TYPE anim)
\r
692 if (!Mesh || Mesh->getMeshType() != EAMT_MD2)
\r
695 IAnimatedMeshMD2* md = (IAnimatedMeshMD2*)Mesh;
\r
697 s32 begin, end, speed;
\r
698 md->getFrameLoop(anim, begin, end, speed);
\r
700 setAnimationSpeed( f32(speed) );
\r
701 setFrameLoop(begin, end);
\r
706 //! Starts a special MD2 animation.
\r
707 bool CAnimatedMeshSceneNode::setMD2Animation(const c8* animationName)
\r
709 if (!Mesh || Mesh->getMeshType() != EAMT_MD2)
\r
712 IAnimatedMeshMD2* md = (IAnimatedMeshMD2*)Mesh;
\r
714 s32 begin, end, speed;
\r
715 if (!md->getFrameLoop(animationName, begin, end, speed))
\r
718 setAnimationSpeed( (f32)speed );
\r
719 setFrameLoop(begin, end);
\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
728 Looping = playAnimationLooped;
\r
731 //! returns the current loop mode
\r
732 bool CAnimatedMeshSceneNode::getLoopMode() const
\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
742 if (callback == LoopCallBack)
\r
746 LoopCallBack->drop();
\r
748 LoopCallBack = callback;
\r
751 LoopCallBack->grab();
\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
758 ReadOnlyMaterials = readonly;
\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
765 return ReadOnlyMaterials;
\r
769 //! Writes attributes of the scene node.
\r
770 void CAnimatedMeshSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
\r
772 IAnimatedMeshSceneNode::serializeAttributes(out, options);
\r
774 if (options && (options->Flags&io::EARWF_USE_RELATIVE_PATHS) && options->Filename)
\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
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
791 //! Reads attributes of the scene node.
\r
792 void CAnimatedMeshSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
\r
794 IAnimatedMeshSceneNode::deserializeAttributes(in, options);
\r
796 io::path oldMeshStr = SceneManager->getMeshCache()->getMeshName(Mesh);
\r
797 io::path newMeshStr = in->getAttributeAsString("Mesh");
\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
805 if (newMeshStr != "" && oldMeshStr != newMeshStr)
\r
807 IAnimatedMesh* newAnimatedMesh = SceneManager->getMesh(newMeshStr.c_str());
\r
809 if (newAnimatedMesh)
\r
810 setMesh(newAnimatedMesh);
\r
813 // TODO: read animation names instead of frame begin and ends
\r
817 //! Sets a new mesh
\r
818 void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh* mesh)
\r
821 return; // won't set null mesh
\r
830 // grab the mesh (it's non-null!)
\r
834 // get materials and bounding box
\r
835 Box = Mesh->getBoundingBox();
\r
837 IMesh* m = Mesh->getMesh(0,0);
\r
841 Materials.reallocate(m->getMeshBufferCount());
\r
843 for (u32 i=0; i<m->getMeshBufferCount(); ++i)
\r
845 IMeshBuffer* mb = m->getMeshBuffer(i);
\r
847 Materials.push_back(mb->getMaterial());
\r
849 Materials.push_back(video::SMaterial());
\r
853 // clean up joint nodes
\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
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
870 return MD3Special ? MD3Special->AbsoluteTagList.get(tagname) : 0;
\r
874 //! updates the absolute position based on the relative and the parents position
\r
875 void CAnimatedMeshSceneNode::updateAbsolutePosition()
\r
877 IAnimatedMeshSceneNode::updateAbsolutePosition();
\r
879 if (!Mesh || Mesh->getMeshType() != EAMT_MD3)
\r
882 SMD3QuaternionTagList *taglist;
\r
883 taglist = ( (IAnimatedMeshMD3*) Mesh )->getTagList ( (s32)getFrameNr(),255,getStartFrame (),getEndFrame () );
\r
888 MD3Special = new SMD3Special();
\r
891 SMD3QuaternionTag parent ( MD3Special->Tagname );
\r
892 if (Parent && Parent->getType() == ESNT_ANIMATED_MESH)
\r
894 const SMD3QuaternionTag * p = ((IAnimatedMeshSceneNode*) Parent)->getMD3TagTransformation
\r
895 ( MD3Special->Tagname );
\r
901 SMD3QuaternionTag relative( RelativeTranslation, RelativeRotation );
\r
903 MD3Special->AbsoluteTagList.set_used ( taglist->size () );
\r
904 for ( u32 i=0; i!= taglist->size (); ++i )
\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
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
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
923 const u32 ttime = (u32)core::floor32(time*1000.0f);
\r
924 if (TransitionTime==ttime)
\r
926 TransitionTime = ttime;
\r
928 setJointMode(EJUOR_CONTROL);
\r
930 setJointMode(EJUOR_NONE);
\r
934 //! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
\r
935 void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)
\r
937 RenderFromIdentity=enable;
\r
941 //! updates the joint positions of this mesh
\r
942 void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)
\r
944 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
\r
947 if (Mesh && Mesh->getMeshType() == EAMT_SKINNED )
\r
950 const f32 frame = getFrameNr(); //old?
\r
952 CSkinnedMesh* skinnedMesh=reinterpret_cast<CSkinnedMesh*>(Mesh);
\r
954 skinnedMesh->transferOnlyJointsHintsToMesh( JointChildSceneNodes );
\r
955 skinnedMesh->animateMesh(frame, 1.0f);
\r
956 skinnedMesh->recoverJointsFromMesh( JointChildSceneNodes);
\r
958 //-----------------------------------------
\r
960 //-----------------------------------------
\r
962 if (Transiting != 0.f)
\r
964 // Init additional matrices
\r
965 if (PretransitingSave.size()<JointChildSceneNodes.size())
\r
967 for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)
\r
968 PretransitingSave.push_back(core::matrix4());
\r
971 for (u32 n=0; n<JointChildSceneNodes.size(); ++n)
\r
973 //------Position------
\r
975 JointChildSceneNodes[n]->setPosition(
\r
977 PretransitingSave[n].getTranslation(),
\r
978 JointChildSceneNodes[n]->getPosition(),
\r
981 //------Rotation------
\r
983 //Code is slow, needs to be fixed up
\r
985 const core::quaternion RotationStart(PretransitingSave[n].getRotationDegrees()*core::DEGTORAD);
\r
986 const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation()*core::DEGTORAD);
\r
988 core::quaternion QRotation;
\r
989 QRotation.slerp(RotationStart, RotationEnd, TransitingBlend);
\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
996 //------Scale------
\r
998 //JointChildSceneNodes[n]->setScale(
\r
1000 // PretransitingSave[n].getScale(),
\r
1001 // JointChildSceneNodes[n]->getScale(),
\r
1002 // TransitingBlend));
\r
1006 if (CalculateAbsolutePositions)
\r
1009 for (u32 n=0;n<JointChildSceneNodes.size();++n)
\r
1011 if (JointChildSceneNodes[n]->getParent()==this)
\r
1013 JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option
\r
1023 void CAnimatedMeshSceneNode::checkJoints()
\r
1025 #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
\r
1029 if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
\r
1034 for (u32 i=0; i<JointChildSceneNodes.size(); ++i)
\r
1035 removeChild(JointChildSceneNodes[i]);
\r
1036 JointChildSceneNodes.clear();
\r
1038 //Create joints for SkinnedMesh
\r
1039 ((CSkinnedMesh*)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);
\r
1040 ((CSkinnedMesh*)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);
\r
1043 JointMode=EJUOR_READ;
\r
1050 void CAnimatedMeshSceneNode::beginTransition()
\r
1055 if (TransitionTime != 0)
\r
1057 //Check the array is big enough
\r
1058 if (PretransitingSave.size()<JointChildSceneNodes.size())
\r
1060 for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)
\r
1061 PretransitingSave.push_back(core::matrix4());
\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
1068 Transiting = core::reciprocal((f32)TransitionTime);
\r
1070 TransitingBlend = 0.f;
\r
1076 ISceneNode* CAnimatedMeshSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)
\r
1079 newParent = Parent;
\r
1081 newManager = SceneManager;
\r
1083 CAnimatedMeshSceneNode* newNode =
\r
1084 new CAnimatedMeshSceneNode(Mesh, NULL, newManager, ID, RelativeTranslation,
\r
1085 RelativeRotation, RelativeScale);
\r
1089 newNode->setParent(newParent); // not in constructor because virtual overload for updateAbsolutePosition won't be called
\r
1093 newNode->cloneMembers(this, newManager);
\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
1125 } // end namespace scene
\r
1126 } // end namespace irr
\r