// Copyright (C) 2002-2011 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CAnimatedMeshSceneNode.h" #include "IVideoDriver.h" #include "ISceneManager.h" #include "S3DVertex.h" #include "os.h" #include "CShadowVolumeSceneNode.h" #include "IAnimatedMeshMD3.h" #include "CSkinnedMesh.h" #include "IDummyTransformationSceneNode.h" #include "IBoneSceneNode.h" #include "IMaterialRenderer.h" #include "IMesh.h" #include "IMeshCache.h" #include "IAnimatedMesh.h" #include "quaternion.h" namespace irr { namespace scene { //! constructor CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh* mesh, ISceneNode* parent, ISceneManager* mgr, s32 id, const core::vector3df& position, const core::vector3df& rotation, const core::vector3df& scale) : IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0), StartFrame(0), EndFrame(0), FramesPerSecond(0.025f), CurrentFrameNr(0.f), LastTimeMs(0), TransitionTime(0), Transiting(0.f), TransitingBlend(0.f), JointMode(EJUOR_NONE), JointsUsed(false), Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false), LoopCallBack(0), PassCount(0), Shadow(0), MD3Special(0) { #ifdef _DEBUG setDebugName("CAnimatedMeshSceneNode"); #endif setMesh(mesh); } //! destructor CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode() { if (MD3Special) MD3Special->drop(); if (Mesh) Mesh->drop(); if (Shadow) Shadow->drop(); if (LoopCallBack) LoopCallBack->drop(); } //! Sets the current frame. From now on the animation is played from this frame. void CAnimatedMeshSceneNode::setCurrentFrame(f32 frame) { // if you pass an out of range value, we just clamp it CurrentFrameNr = core::clamp ( frame, (f32)StartFrame, (f32)EndFrame ); beginTransition(); //transit to this frame if enabled } //! Returns the currently displayed frame number. f32 CAnimatedMeshSceneNode::getFrameNr() const { return CurrentFrameNr; } void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs) { if (Transiting!=0.f) { TransitingBlend += (f32)(timeMs) * Transiting; if (TransitingBlend > 1.f) { Transiting=0.f; TransitingBlend=0.f; } } if ((StartFrame==EndFrame)) { CurrentFrameNr = (f32)StartFrame; //Support for non animated meshes } else if (Looping) { // play animation looped CurrentFrameNr += timeMs * FramesPerSecond; // We have no interpolation between EndFrame and StartFrame, // the last frame must be identical to first one with our current solution. if (FramesPerSecond > 0.f) //forwards... { if (CurrentFrameNr > EndFrame) CurrentFrameNr = StartFrame + fmod(CurrentFrameNr - StartFrame, (f32)(EndFrame-StartFrame)); } else //backwards... { if (CurrentFrameNr < StartFrame) CurrentFrameNr = EndFrame - fmod(EndFrame - CurrentFrameNr, (f32)(EndFrame-StartFrame)); } } else { // play animation non looped CurrentFrameNr += timeMs * FramesPerSecond; if (FramesPerSecond > 0.f) //forwards... { if (CurrentFrameNr > (f32)EndFrame) { CurrentFrameNr = (f32)EndFrame; if (LoopCallBack) LoopCallBack->OnAnimationEnd(this); } } else //backwards... { if (CurrentFrameNr < (f32)StartFrame) { CurrentFrameNr = (f32)StartFrame; if (LoopCallBack) LoopCallBack->OnAnimationEnd(this); } } } } void CAnimatedMeshSceneNode::OnRegisterSceneNode() { if (IsVisible) { // because this node supports rendering of mixed mode meshes consisting of // transparent and solid material at the same time, we need to go through all // materials, check of what type they are and register this node for the right // render pass according to that. video::IVideoDriver* driver = SceneManager->getVideoDriver(); PassCount = 0; int transparentCount = 0; int solidCount = 0; // count transparent and solid materials in this scene node for (u32 i=0; i<Materials.size(); ++i) { video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType); if (rnd && rnd->isTransparent()) ++transparentCount; else ++solidCount; if (solidCount && transparentCount) break; } // register according to material types counted if (solidCount) SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID); if (transparentCount) SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); ISceneNode::OnRegisterSceneNode(); } } IMesh * CAnimatedMeshSceneNode::getMeshForCurrentFrame() { if(Mesh->getMeshType() != EAMT_SKINNED) { s32 frameNr = (s32) getFrameNr(); s32 frameBlend = (s32) (core::fract ( getFrameNr() ) * 1000.f); return Mesh->getMesh(frameNr, frameBlend, StartFrame, EndFrame); } else { #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ return 0; #endif // As multiple scene nodes may be sharing the same skinned mesh, we have to // re-animate it every frame to ensure that this node gets the mesh that it needs. CSkinnedMesh* skinnedMesh = reinterpret_cast<CSkinnedMesh*>(Mesh); if (JointMode == EJUOR_CONTROL)//write to mesh skinnedMesh->transferJointsToMesh(JointChildSceneNodes); else skinnedMesh->animateMesh(getFrameNr(), 1.0f); // Update the skinned mesh for the current joint transforms. skinnedMesh->skinMesh(); if (JointMode == EJUOR_READ)//read from mesh { skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes); //---slow--- for (u32 n=0;n<JointChildSceneNodes.size();++n) if (JointChildSceneNodes[n]->getParent()==this) { JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option } } if(JointMode == EJUOR_CONTROL) { // For meshes other than EJUOR_CONTROL, this is done by calling animateMesh() skinnedMesh->updateBoundingBox(); } return skinnedMesh; } } //! OnAnimate() is called just before rendering the whole scene. void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs) { buildFrameNr(timeMs-LastTimeMs); if (Mesh) { scene::IMesh * mesh = getMeshForCurrentFrame(); if (mesh) Box = mesh->getBoundingBox(); } LastTimeMs = timeMs; IAnimatedMeshSceneNode::OnAnimate ( timeMs ); } //! renders the node. void CAnimatedMeshSceneNode::render() { video::IVideoDriver* driver = SceneManager->getVideoDriver(); if (!Mesh || !driver) return; bool isTransparentPass = SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; ++PassCount; scene::IMesh* m = getMeshForCurrentFrame(); if(m) { Box = m->getBoundingBox(); } else { #ifdef _DEBUG os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING); #endif } driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); if (Shadow && PassCount==1) Shadow->updateShadowVolumes(); // for debug purposes only: bool renderMeshes = true; video::SMaterial mat; if (DebugDataVisible && PassCount==1) { // overwrite half transparency if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY) { for (u32 i=0; i<m->getMeshBufferCount(); ++i) { scene::IMeshBuffer* mb = m->getMeshBuffer(i); mat = ReadOnlyMaterials ? mb->getMaterial() : Materials[i]; mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; if (RenderFromIdentity) driver->setTransform(video::ETS_WORLD, core::IdentityMatrix ); else if (Mesh->getMeshType() == EAMT_SKINNED) driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation); driver->setMaterial(mat); driver->drawMeshBuffer(mb); } renderMeshes = false; } } // render original meshes if (renderMeshes) { for (u32 i=0; i<m->getMeshBufferCount(); ++i) { video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType); bool transparent = (rnd && rnd->isTransparent()); // only render transparent buffer if this is the transparent render pass // and solid only in solid pass if (transparent == isTransparentPass) { scene::IMeshBuffer* mb = m->getMeshBuffer(i); const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i]; if (RenderFromIdentity) driver->setTransform(video::ETS_WORLD, core::IdentityMatrix ); else if (Mesh->getMeshType() == EAMT_SKINNED) driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation); driver->setMaterial(material); driver->drawMeshBuffer(mb); } } } driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); // for debug purposes only: if (DebugDataVisible && PassCount==1) { video::SMaterial debug_mat; debug_mat.Lighting = false; debug_mat.AntiAliasing=0; driver->setMaterial(debug_mat); // show normals if (DebugDataVisible & scene::EDS_NORMALS) { core::vector3df normalizedNormal; const f32 DebugNormalLength = SceneManager->getParameters()->getAttributeAsFloat(DEBUG_NORMAL_LENGTH); const video::SColor DebugNormalColor = SceneManager->getParameters()->getAttributeAsColor(DEBUG_NORMAL_COLOR); // draw normals for (u32 g=0; g < m->getMeshBufferCount(); ++g) { const scene::IMeshBuffer* mb = m->getMeshBuffer(g); const u32 vSize = video::getVertexPitchFromType(mb->getVertexType()); const video::S3DVertex* v = ( const video::S3DVertex*)mb->getVertices(); const bool normalize = mb->getMaterial().NormalizeNormals; for (u32 i=0; i != mb->getVertexCount(); ++i) { normalizedNormal = v->Normal; if (normalize) normalizedNormal.normalize(); driver->draw3DLine(v->Pos, v->Pos + (normalizedNormal * DebugNormalLength), DebugNormalColor); v = (const video::S3DVertex*) ( (u8*) v+vSize ); } } driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); } debug_mat.ZBuffer = video::ECFN_NEVER; debug_mat.Lighting = false; driver->setMaterial(debug_mat); if (DebugDataVisible & scene::EDS_BBOX) driver->draw3DBox(Box, video::SColor(255,255,255,255)); // show bounding box if (DebugDataVisible & scene::EDS_BBOX_BUFFERS) { for (u32 g=0; g< m->getMeshBufferCount(); ++g) { const IMeshBuffer* mb = m->getMeshBuffer(g); if (Mesh->getMeshType() == EAMT_SKINNED) driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation); driver->draw3DBox( mb->getBoundingBox(), video::SColor(255,190,128,128) ); } } // show skeleton if (DebugDataVisible & scene::EDS_SKELETON) { if (Mesh->getMeshType() == EAMT_SKINNED) { // draw skeleton for (u32 g=0; g < ((ISkinnedMesh*)Mesh)->getAllJoints().size(); ++g) { ISkinnedMesh::SJoint *joint=((ISkinnedMesh*)Mesh)->getAllJoints()[g]; for (u32 n=0;n<joint->Children.size();++n) { driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(), joint->Children[n]->GlobalAnimatedMatrix.getTranslation(), video::SColor(255,51,66,255)); } } } // show tag for quake3 models if (Mesh->getMeshType() == EAMT_MD3) { IAnimatedMesh * arrow = SceneManager->addArrowMesh ( "__tag_show", 0xFF0000FF, 0xFF000088, 4, 8, 5.f, 4.f, 0.5f, 1.f); if (!arrow) { arrow = SceneManager->getMesh ( "__tag_show" ); } IMesh *arrowMesh = arrow->getMesh(0); core::matrix4 matr; SMD3QuaternionTagList *taglist = ((IAnimatedMeshMD3*)Mesh)->getTagList( (s32)getFrameNr(), 255, getStartFrame(), getEndFrame()); if (taglist) { for ( u32 ts = 0; ts != taglist->size(); ++ts ) { (*taglist)[ts].setto(matr); driver->setTransform(video::ETS_WORLD, matr ); for ( u32 a = 0; a != arrowMesh->getMeshBufferCount(); ++a ) driver->drawMeshBuffer(arrowMesh->getMeshBuffer(a)); } } } } // show mesh if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY) { debug_mat.Lighting = false; debug_mat.Wireframe = true; debug_mat.ZBuffer = video::ECFN_NEVER; driver->setMaterial(debug_mat); for (u32 g=0; g<m->getMeshBufferCount(); ++g) { const IMeshBuffer* mb = m->getMeshBuffer(g); if (RenderFromIdentity) driver->setTransform(video::ETS_WORLD, core::IdentityMatrix ); else if (Mesh->getMeshType() == EAMT_SKINNED) driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation); driver->drawMeshBuffer(mb); } } } } //! Returns the current start frame number. s32 CAnimatedMeshSceneNode::getStartFrame() const { return StartFrame; } //! Returns the current start frame number. s32 CAnimatedMeshSceneNode::getEndFrame() const { return EndFrame; } //! sets the frames between the animation is looped. //! the default is 0 - MaximalFrameCount of the mesh. bool CAnimatedMeshSceneNode::setFrameLoop(s32 begin, s32 end) { const s32 maxFrameCount = Mesh->getFrameCount() - 1; if (end < begin) { StartFrame = core::s32_clamp(end, 0, maxFrameCount); EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount); } else { StartFrame = core::s32_clamp(begin, 0, maxFrameCount); EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount); } if (FramesPerSecond < 0) setCurrentFrame((f32)EndFrame); else setCurrentFrame((f32)StartFrame); return true; } //! sets the speed with witch the animation is played void CAnimatedMeshSceneNode::setAnimationSpeed(f32 framesPerSecond) { FramesPerSecond = framesPerSecond * 0.001f; } f32 CAnimatedMeshSceneNode::getAnimationSpeed() const { return FramesPerSecond * 1000.f; } //! returns the axis aligned bounding box of this node const core::aabbox3d<f32>& CAnimatedMeshSceneNode::getBoundingBox() const { return Box; } //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). //! This function is needed for inserting the node into the scene hirachy on a //! optimal position for minimizing renderstate changes, but can also be used //! to directly modify the material of a scene node. video::SMaterial& CAnimatedMeshSceneNode::getMaterial(u32 i) { if (i >= Materials.size()) return ISceneNode::getMaterial(i); return Materials[i]; } //! returns amount of materials used by this scene node. u32 CAnimatedMeshSceneNode::getMaterialCount() const { return Materials.size(); } //! Creates shadow volume scene node as child of this node //! and returns a pointer to it. IShadowVolumeSceneNode* CAnimatedMeshSceneNode::addShadowVolumeSceneNode( const IMesh* shadowMesh, s32 id, bool zfailmethod, f32 infinity) { if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER)) return 0; if (!shadowMesh) shadowMesh = Mesh; // if null is given, use the mesh of node if (Shadow) Shadow->drop(); Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id, zfailmethod, infinity); return Shadow; } //! Returns a pointer to a child node, which has the same transformation as //! the corresponding joint, if the mesh in this scene node is a skinned mesh. IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(const c8* jointName) { #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING); return 0; #endif if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) { os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING); return 0; } checkJoints(); ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh; const s32 number = skinnedMesh->getJointNumber(jointName); if (number == -1) { os::Printer::log("Joint with specified name not found in skinned mesh", jointName, ELL_DEBUG); return 0; } if ((s32)JointChildSceneNodes.size() <= number) { os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING); return 0; } return JointChildSceneNodes[number]; } //! Returns a pointer to a child node, which has the same transformation as //! the corresponding joint, if the mesh in this scene node is a skinned mesh. IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(u32 jointID) { #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING); return 0; #endif if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) { os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING); return 0; } checkJoints(); if (JointChildSceneNodes.size() <= jointID) { os::Printer::log("Joint not loaded into node", ELL_WARNING); return 0; } return JointChildSceneNodes[jointID]; } //! Gets joint count. u32 CAnimatedMeshSceneNode::getJointCount() const { #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ return 0; #endif if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) return 0; ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh; return skinnedMesh->getJointCount(); } //! Returns a pointer to a child node, which has the same transformation as //! the corresponding joint, if the mesh in this scene node is a ms3d mesh. ISceneNode* CAnimatedMeshSceneNode::getMS3DJointNode(const c8* jointName) { return getJointNode(jointName); } //! Returns a pointer to a child node, which has the same transformation as //! the corresponding joint, if the mesh in this scene node is a .x mesh. ISceneNode* CAnimatedMeshSceneNode::getXJointNode(const c8* jointName) { return getJointNode(jointName); } //! Removes a child from this scene node. //! Implemented here, to be able to remove the shadow properly, if there is one, //! or to remove attached childs. bool CAnimatedMeshSceneNode::removeChild(ISceneNode* child) { if (child && Shadow == child) { Shadow->drop(); Shadow = 0; } if (ISceneNode::removeChild(child)) { if (JointsUsed) //stop weird bugs caused while changing parents as the joints are being created { for (u32 i=0; i<JointChildSceneNodes.size(); ++i) { if (JointChildSceneNodes[i] == child) { JointChildSceneNodes[i] = 0; //remove link to child break; } } } return true; } return false; } //! Starts a MD2 animation. bool CAnimatedMeshSceneNode::setMD2Animation(EMD2_ANIMATION_TYPE anim) { if (!Mesh || Mesh->getMeshType() != EAMT_MD2) return false; IAnimatedMeshMD2* md = (IAnimatedMeshMD2*)Mesh; s32 begin, end, speed; md->getFrameLoop(anim, begin, end, speed); setAnimationSpeed( f32(speed) ); setFrameLoop(begin, end); return true; } //! Starts a special MD2 animation. bool CAnimatedMeshSceneNode::setMD2Animation(const c8* animationName) { if (!Mesh || Mesh->getMeshType() != EAMT_MD2) return false; IAnimatedMeshMD2* md = (IAnimatedMeshMD2*)Mesh; s32 begin, end, speed; if (!md->getFrameLoop(animationName, begin, end, speed)) return false; setAnimationSpeed( (f32)speed ); setFrameLoop(begin, end); return true; } //! Sets looping mode which is on by default. If set to false, //! animations will not be looped. void CAnimatedMeshSceneNode::setLoopMode(bool playAnimationLooped) { Looping = playAnimationLooped; } //! Sets a callback interface which will be called if an animation //! playback has ended. Set this to 0 to disable the callback again. void CAnimatedMeshSceneNode::setAnimationEndCallback(IAnimationEndCallBack* callback) { if (callback == LoopCallBack) return; if (LoopCallBack) LoopCallBack->drop(); LoopCallBack = callback; if (LoopCallBack) LoopCallBack->grab(); } //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style. void CAnimatedMeshSceneNode::setReadOnlyMaterials(bool readonly) { ReadOnlyMaterials = readonly; } //! Returns if the scene node should not copy the materials of the mesh but use them in a read only style bool CAnimatedMeshSceneNode::isReadOnlyMaterials() const { return ReadOnlyMaterials; } //! Writes attributes of the scene node. void CAnimatedMeshSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const { IAnimatedMeshSceneNode::serializeAttributes(out, options); out->addString("Mesh", SceneManager->getMeshCache()->getMeshName(Mesh).getPath().c_str()); out->addBool("Looping", Looping); out->addBool("ReadOnlyMaterials", ReadOnlyMaterials); out->addFloat("FramesPerSecond", FramesPerSecond); out->addInt("StartFrame", StartFrame); out->addInt("EndFrame", EndFrame); } //! Reads attributes of the scene node. void CAnimatedMeshSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) { IAnimatedMeshSceneNode::deserializeAttributes(in, options); io::path oldMeshStr = SceneManager->getMeshCache()->getMeshName(Mesh); io::path newMeshStr = in->getAttributeAsString("Mesh"); Looping = in->getAttributeAsBool("Looping"); ReadOnlyMaterials = in->getAttributeAsBool("ReadOnlyMaterials"); FramesPerSecond = in->getAttributeAsFloat("FramesPerSecond"); StartFrame = in->getAttributeAsInt("StartFrame"); EndFrame = in->getAttributeAsInt("EndFrame"); if (newMeshStr != "" && oldMeshStr != newMeshStr) { IAnimatedMesh* newAnimatedMesh = SceneManager->getMesh(newMeshStr.c_str()); if (newAnimatedMesh) setMesh(newAnimatedMesh); } // TODO: read animation names instead of frame begin and ends } //! Sets a new mesh void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh* mesh) { if (!mesh) return; // won't set null mesh if (Mesh != mesh) { if (Mesh) Mesh->drop(); Mesh = mesh; // grab the mesh (it's non-null!) Mesh->grab(); } // get materials and bounding box Box = Mesh->getBoundingBox(); IMesh* m = Mesh->getMesh(0,0); if (m) { Materials.clear(); Materials.reallocate(m->getMeshBufferCount()); for (u32 i=0; i<m->getMeshBufferCount(); ++i) { IMeshBuffer* mb = m->getMeshBuffer(i); if (mb) Materials.push_back(mb->getMaterial()); else Materials.push_back(video::SMaterial()); } } // clean up joint nodes if (JointsUsed) { JointsUsed=false; checkJoints(); } // get start and begin time // setAnimationSpeed(Mesh->getAnimationSpeed()); setFrameLoop(0, Mesh->getFrameCount()); } // returns the absolute transformation for a special MD3 Tag if the mesh is a md3 mesh, // or the absolutetransformation if it's a normal scenenode const SMD3QuaternionTag* CAnimatedMeshSceneNode::getMD3TagTransformation(const core::stringc& tagname) { return MD3Special ? MD3Special->AbsoluteTagList.get(tagname) : 0; } //! updates the absolute position based on the relative and the parents position void CAnimatedMeshSceneNode::updateAbsolutePosition() { IAnimatedMeshSceneNode::updateAbsolutePosition(); if (!Mesh || Mesh->getMeshType() != EAMT_MD3) return; SMD3QuaternionTagList *taglist; taglist = ( (IAnimatedMeshMD3*) Mesh )->getTagList ( (s32)getFrameNr(),255,getStartFrame (),getEndFrame () ); if (taglist) { if (!MD3Special) { MD3Special = new SMD3Special(); } SMD3QuaternionTag parent ( MD3Special->Tagname ); if (Parent && Parent->getType() == ESNT_ANIMATED_MESH) { const SMD3QuaternionTag * p = ((IAnimatedMeshSceneNode*) Parent)->getMD3TagTransformation ( MD3Special->Tagname ); if (p) parent = *p; } SMD3QuaternionTag relative( RelativeTranslation, RelativeRotation ); MD3Special->AbsoluteTagList.set_used ( taglist->size () ); for ( u32 i=0; i!= taglist->size (); ++i ) { MD3Special->AbsoluteTagList[i].position = parent.position + (*taglist)[i].position + relative.position; MD3Special->AbsoluteTagList[i].rotation = parent.rotation * (*taglist)[i].rotation * relative.rotation; } } } //! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set) void CAnimatedMeshSceneNode::setJointMode(E_JOINT_UPDATE_ON_RENDER mode) { checkJoints(); JointMode=mode; } //! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2) //! you must call animateJoints(), or the mesh will not animate void CAnimatedMeshSceneNode::setTransitionTime(f32 time) { const u32 ttime = (u32)core::floor32(time*1000.0f); if (TransitionTime==ttime) return; TransitionTime = ttime; if (ttime != 0) setJointMode(EJUOR_CONTROL); else setJointMode(EJUOR_NONE); } //! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected) void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable) { RenderFromIdentity=enable; } //! updates the joint positions of this mesh void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions) { #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ return; #endif if (Mesh && Mesh->getMeshType() == EAMT_SKINNED ) { checkJoints(); const f32 frame = getFrameNr(); //old? CSkinnedMesh* skinnedMesh=reinterpret_cast<CSkinnedMesh*>(Mesh); skinnedMesh->transferOnlyJointsHintsToMesh( JointChildSceneNodes ); skinnedMesh->animateMesh(frame, 1.0f); skinnedMesh->recoverJointsFromMesh( JointChildSceneNodes); //----------------------------------------- // Transition //----------------------------------------- if (Transiting != 0.f) { // Init additional matrices if (PretransitingSave.size()<JointChildSceneNodes.size()) { for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n) PretransitingSave.push_back(core::matrix4()); } for (u32 n=0; n<JointChildSceneNodes.size(); ++n) { //------Position------ JointChildSceneNodes[n]->setPosition( core::lerp( PretransitingSave[n].getTranslation(), JointChildSceneNodes[n]->getPosition(), TransitingBlend)); //------Rotation------ //Code is slow, needs to be fixed up const core::quaternion RotationStart(PretransitingSave[n].getRotationDegrees()*core::DEGTORAD); const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation()*core::DEGTORAD); core::quaternion QRotation; QRotation.slerp(RotationStart, RotationEnd, TransitingBlend); core::vector3df tmpVector; QRotation.toEuler(tmpVector); tmpVector*=core::RADTODEG; //convert from radians back to degrees JointChildSceneNodes[n]->setRotation( tmpVector ); //------Scale------ //JointChildSceneNodes[n]->setScale( // core::lerp( // PretransitingSave[n].getScale(), // JointChildSceneNodes[n]->getScale(), // TransitingBlend)); } } if (CalculateAbsolutePositions) { //---slow--- for (u32 n=0;n<JointChildSceneNodes.size();++n) { if (JointChildSceneNodes[n]->getParent()==this) { JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option } } } } } /*! */ void CAnimatedMeshSceneNode::checkJoints() { #ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_ return; #endif if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) return; if (!JointsUsed) { for (u32 i=0; i<JointChildSceneNodes.size(); ++i) removeChild(JointChildSceneNodes[i]); JointChildSceneNodes.clear(); //Create joints for SkinnedMesh ((CSkinnedMesh*)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager); ((CSkinnedMesh*)Mesh)->recoverJointsFromMesh(JointChildSceneNodes); JointsUsed=true; JointMode=EJUOR_READ; } } /*! */ void CAnimatedMeshSceneNode::beginTransition() { if (!JointsUsed) return; if (TransitionTime != 0) { //Check the array is big enough if (PretransitingSave.size()<JointChildSceneNodes.size()) { for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n) PretransitingSave.push_back(core::matrix4()); } //Copy the position of joints for (u32 n=0;n<JointChildSceneNodes.size();++n) PretransitingSave[n]=JointChildSceneNodes[n]->getRelativeTransformation(); Transiting = core::reciprocal((f32)TransitionTime); } TransitingBlend = 0.f; } /*! */ ISceneNode* CAnimatedMeshSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager) { if (!newParent) newParent = Parent; if (!newManager) newManager = SceneManager; CAnimatedMeshSceneNode* newNode = new CAnimatedMeshSceneNode(Mesh, NULL, newManager, ID, RelativeTranslation, RelativeRotation, RelativeScale); if (newParent) { newNode->setParent(newParent); // not in constructor because virtual overload for updateAbsolutePosition won't be called newNode->drop(); } newNode->cloneMembers(this, newManager); newNode->Materials = Materials; newNode->Box = Box; newNode->Mesh = Mesh; newNode->StartFrame = StartFrame; newNode->EndFrame = EndFrame; newNode->FramesPerSecond = FramesPerSecond; newNode->CurrentFrameNr = CurrentFrameNr; newNode->JointMode = JointMode; newNode->JointsUsed = JointsUsed; newNode->TransitionTime = TransitionTime; newNode->Transiting = Transiting; newNode->TransitingBlend = TransitingBlend; newNode->Looping = Looping; newNode->ReadOnlyMaterials = ReadOnlyMaterials; newNode->LoopCallBack = LoopCallBack; newNode->PassCount = PassCount; newNode->Shadow = Shadow; newNode->JointChildSceneNodes = JointChildSceneNodes; newNode->PretransitingSave = PretransitingSave; newNode->RenderFromIdentity = RenderFromIdentity; newNode->MD3Special = MD3Special; return newNode; } } // end namespace scene } // end namespace irr
Options | Liens officiels | Caractéristiques | Statistiques | Communauté |
---|---|---|---|---|
Corrections |
|
xhtml 1.0 css 2.1 Propulsé par FluxBB Traduit par FluxBB.fr |
882 membres 1429 sujets 11119 messages |
Dernier membre inscrit: LiseBuisson96 75 invités en ligne Aucun membre connecté RSS Feed |