做者: i_dovelemonnode
日期: 2014 / 12 / 17windows
来源: CSDN框架
主题: Custom Animator, Referenced countdom
在昨天的文章《Irrlicht 3D Engine 笔记系列 之 教程4 - Movement》中,博主向你们保证会在今天向你们实际操做怎样扩展Irrlicht引擎的Animator特性。假设读者对Irrlicht的Animator的特性不是很是了解的话,请先了解下前面的那篇文章,本片文章是在上次文章的基础上进行的。ide
通过昨天咱们的分析,发现仅仅要咱们继承ISceneNodeAnimator这个接口来实现咱们本身的Animator就可以了。因此如下,咱们就按着这个思路来对建立咱们本身定义的Animator。函数
首先,对咱们将要建立的Animator进行下简单的描写叙述:oop
博主想要建立一个沿着某条直线进行循环运动,并且物体自己在旋转的Animator。博主称之为Roll Animator(翻滚动画)。post
好了。在明白了咱们想要建立的Animator的功能以后。咱们就实际动手来建立一个。学习
首先,咱们来申明一个CMyRollAnimator的类,这个类继承至ISceneNodeAnimator,并且复写当中的两个纯虚拟函数animateNode和createClone,申明头文件例如如下所看到的:动画
//------------------------------------------------------------------------------------------------- // declaration : Copyright (c), by XJ , 2014. All right reserved . // brief : This file will define the custom roll animator. // author : XJ // date : 2014 / 12 / 17 // version : 1.0 //-------------------------------------------------------------------------------------------------- #pragma once #include<irrlicht.h> using namespace irr ; using namespace scene ; using namespace core; class CMyRollAnimator: public ISceneNodeAnimator { public: CMyRollAnimator(vector3df _startPos, vector3df _endPos, f32 _speed); //animate a node virtual void animateNode(ISceneNode* node, u32 timeMs); //clone a animator virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0); private: vector3df m_vStartPos ; vector3df m_vEndPos ; vector3df m_vCurPos ; f32 m_fSpeed ; u32 m_uCurTime; //internal usage vector3df m_vDir ; vector3df m_vRDir ; };读者可以看到博主上面定义的类很的简单,仅仅是加入了几个成员属性而已。除此以外就是复写的方法和一个构造函数。
为了实现前面描写叙述的运动,咱们来介绍下这里面成员的含义。首先,咱们需要知道物体运动应该从哪里開始。到哪里结束,并且需要知道这个运动的速度以及当前运动的时间。另外两个成员属性用于内部计算使用。如下来看看这个类的明白实现是怎么样的:
#include"CMyRollAnimator.h" //constructor CMyRollAnimator::CMyRollAnimator(vector3df _startPos, vector3df _endPos, f32 _speed) :m_vStartPos(_startPos), m_vEndPos(_endPos), m_fSpeed(_speed), m_vCurPos(_startPos), m_uCurTime(0) { m_vDir = m_vEndPos - m_vStartPos ; m_vDir.normalize(); m_vRDir = m_vDir ; m_vRDir.rotateXZBy(90.0f); } //animate a node void CMyRollAnimator::animateNode(ISceneNode* _pNode, u32 timeMs) { if(0 == m_uCurTime) { m_uCurTime = timeMs ; _pNode->setPosition(m_vStartPos); } else { u32 _deltaTime = timeMs - m_uCurTime ; f32 _fDeltaTime = _deltaTime / 1000.0f ; m_uCurTime = timeMs ; m_vCurPos += _fDeltaTime * m_fSpeed * m_vDir ; if(abs(m_vCurPos.X - m_vEndPos.X) < 0.01f && abs(m_vCurPos.Y - m_vEndPos.Y) < 0.01f && abs(m_vCurPos.Z - m_vEndPos.Z) < 0.01f) { m_vCurPos = m_vStartPos ; } _pNode->setPosition(m_vCurPos); _pNode->setRotation(-m_vRDir * 180.0f * timeMs/1000.0f); } }// end for animateNode //clone an animator ISceneNodeAnimator* CMyRollAnimator::createClone(ISceneNode* node, ISceneManager* newManager) { CMyRollAnimator* _pAnimator = new CMyRollAnimator(m_vStartPos, m_vEndPos, m_fSpeed); return _pAnimator ; }// end for createClone
因此读者大概。就行明确博主为何要在类内部定义的当前时间了。咱们经过这个所谓的当前时间来与系统实际的当前时间进行比較,计算出时间间隔,从而控制节点进行移动。
想要实现本身的Animator是否是十分的简单啊???仅仅要掌握了这个主要的规则。咱们就可以发挥咱们本身的想象来制做出十分复杂的Animator出来。
你们可以借鉴下cocos2d-x里面的Action,试着在Irrlicht中也实现它们,并且将代码共享出来给你们使用。假设有空的话,博主可能会本身实现一些Animator共享给你们使用。
完整的代码和教程4中的代码一致。所不一样的是将FlyCircleAnimator换成咱们这里自定义的Animator而已,例如如下所看到的:
#include<irrlicht.h> #include"MyEventReceiver.h" #include"CMyRollAnimator.h" using namespace irr; using namespace core; using namespace gui; using namespace scene; using namespace video; #ifdef _IRR_WINDOWS_ #pragma comment(lib,"irrlicht.lib") #pragma comment(linker,"/subsystem:windows /ENTRY:mainCRTStartup") #endif int main() { //Create the irrdevice MyEventReceiver _receiver; IrrlichtDevice* _pDevice = createDevice(EDT_DIRECT3D9,dimension2d<u32>(800,640),32U, false, false, false, &_receiver); //Check if create successfully if(NULL == _pDevice) return 1 ; //Get the video driver and scene manager IVideoDriver* _pVideoDriver = _pDevice->getVideoDriver(); ISceneManager* _pSceneManager = _pDevice->getSceneManager(); //Create a sphere node ISceneNode* _pNode = _pSceneManager->addSphereSceneNode(); //Check if create successfully if(NULL == _pNode) return 1 ; _pNode->setPosition(vector3df(0,0,30)); _pNode->setMaterialTexture(0,_pVideoDriver->getTexture("wall.bmp")); _pNode->setMaterialFlag(EMF_LIGHTING,false); //Create a cube node ISceneNode* _pCubeNode = _pSceneManager->addCubeSceneNode(); //Check if create successfully if(NULL == _pCubeNode) return 1 ; _pCubeNode->setMaterialTexture(0,_pVideoDriver->getTexture("t351sml.jpg")); _pCubeNode->setMaterialFlag(EMF_LIGHTING, false); //Create a scene node animator for cube node //ISceneNodeAnimator* _pAnimator = _pSceneManager->createFlyCircleAnimator(vector3df(0,0,30), // 20.0f); CMyRollAnimator * _pAnimator = new CMyRollAnimator(vector3df(-20,0,30), vector3df(20,0,30), 10.0f); //Check if create successfully if(NULL == _pAnimator) return 1 ; _pCubeNode->addAnimator(_pAnimator); //Drop the animator _pAnimator->drop(); //Add one camera node _pSceneManager->addCameraSceneNode(); int _nlastFPS = -1 ; u32 _uLastTime = _pDevice->getTimer()->getTime(); const f32 MOVEMENT_SPEED = 5.0f ; //Do loop while(_pDevice->run()) { const u32 _now = _pDevice->getTimer()->getTime(); const f32 _frameDeltaTime = (f32)(_now - _uLastTime)/1000.0f; _uLastTime = _now ; vector3df _pos = _pNode->getPosition(); if(_receiver.IsKeyDown(KEY_KEY_W)) _pos.Y += MOVEMENT_SPEED * _frameDeltaTime ; else if(_receiver.IsKeyDown(KEY_KEY_S)) _pos.Y -= MOVEMENT_SPEED * _frameDeltaTime ; if(_receiver.IsKeyDown(KEY_KEY_A)) _pos.X -= MOVEMENT_SPEED * _frameDeltaTime ; else if(_receiver.IsKeyDown(KEY_KEY_D)) _pos.X += MOVEMENT_SPEED * _frameDeltaTime ; _pNode->setPosition(_pos); //Draw the scene _pVideoDriver->beginScene(); _pSceneManager->drawAll(); _pVideoDriver->endScene(); }// end while _pDevice->drop(); return 0 ; }// end
博主在编写这个实验程序的时候。发现当咱们调用_pAnimator->drop的时候,animator并无销毁。因此,博主对于引擎内部是怎样保存一个对象技术感到好奇。决心研究一下。
在Irrlicht引擎中,经过一个十分简单的引用技术(Referenced Counter)系统来对系统中的对象进行跟踪。
这个功能是经过一个名为IReferenceCounted接口来实现的,引擎中大部分的类都继承了这个接口。从而实现了引用技术操做。如下来看下。这个类的申明:
// Copyright (C) 2002-2012 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #ifndef __I_IREFERENCE_COUNTED_H_INCLUDED__ #define __I_IREFERENCE_COUNTED_H_INCLUDED__ #include "irrTypes.h" namespace irr { //! Base class of most objects of the Irrlicht Engine. /** This class provides reference counting through the methods grab() and drop(). It also is able to store a debug string for every instance of an object. Most objects of the Irrlicht Engine are derived from IReferenceCounted, and so they are reference counted. When you create an object in the Irrlicht engine, calling a method which starts with 'create', an object is created, and you get a pointer to the new object. If you no longer need the object, you have to call drop(). This will destroy the object, if grab() was not called in another part of you program, because this part still needs the object. Note, that you only need to call drop() to the object, if you created it, and the method had a 'create' in it. A simple example: If you want to create a texture, you may want to call an imaginable method IDriver::createTexture. You call ITexture* texture = driver->createTexture(dimension2d<u32>(128, 128)); If you no longer need the texture, call texture->drop(). If you want to load a texture, you may want to call imaginable method IDriver::loadTexture. You do this like ITexture* texture = driver->loadTexture("example.jpg"); You will not have to drop the pointer to the loaded texture, because the name of the method does not start with 'create'. The texture is stored somewhere by the driver. */ class IReferenceCounted { public: //! Constructor. IReferenceCounted() : DebugName(0), ReferenceCounter(1) { } //! Destructor. virtual ~IReferenceCounted() { } //! Grabs the object. Increments the reference counter by one. /** Someone who calls grab() to an object, should later also call drop() to it. If an object never gets as much drop() as grab() calls, it will never be destroyed. The IReferenceCounted class provides a basic reference counting mechanism with its methods grab() and drop(). Most objects of the Irrlicht Engine are derived from IReferenceCounted, and so they are reference counted. When you create an object in the Irrlicht engine, calling a method which starts with 'create', an object is created, and you get a pointer to the new object. If you no longer need the object, you have to call drop(). This will destroy the object, if grab() was not called in another part of you program, because this part still needs the object. Note, that you only need to call drop() to the object, if you created it, and the method had a 'create' in it. A simple example: If you want to create a texture, you may want to call an imaginable method IDriver::createTexture. You call ITexture* texture = driver->createTexture(dimension2d<u32>(128, 128)); If you no longer need the texture, call texture->drop(). If you want to load a texture, you may want to call imaginable method IDriver::loadTexture. You do this like ITexture* texture = driver->loadTexture("example.jpg"); You will not have to drop the pointer to the loaded texture, because the name of the method does not start with 'create'. The texture is stored somewhere by the driver. */ void grab() const { ++ReferenceCounter; } //! Drops the object. Decrements the reference counter by one. /** The IReferenceCounted class provides a basic reference counting mechanism with its methods grab() and drop(). Most objects of the Irrlicht Engine are derived from IReferenceCounted, and so they are reference counted. When you create an object in the Irrlicht engine, calling a method which starts with 'create', an object is created, and you get a pointer to the new object. If you no longer need the object, you have to call drop(). This will destroy the object, if grab() was not called in another part of you program, because this part still needs the object. Note, that you only need to call drop() to the object, if you created it, and the method had a 'create' in it. A simple example: If you want to create a texture, you may want to call an imaginable method IDriver::createTexture. You call ITexture* texture = driver->createTexture(dimension2d<u32>(128, 128)); If you no longer need the texture, call texture->drop(). If you want to load a texture, you may want to call imaginable method IDriver::loadTexture. You do this like ITexture* texture = driver->loadTexture("example.jpg"); You will not have to drop the pointer to the loaded texture, because the name of the method does not start with 'create'. The texture is stored somewhere by the driver. \return True, if the object was deleted. */ bool drop() const { // someone is doing bad reference counting. _IRR_DEBUG_BREAK_IF(ReferenceCounter <= 0) --ReferenceCounter; if (!ReferenceCounter) { delete this; return true; } return false; } //! Get the reference count. /** \return Current value of the reference counter. */ s32 getReferenceCount() const { return ReferenceCounter; } //! Returns the debug name of the object. /** The Debugname may only be set and changed by the object itself. This method should only be used in Debug mode. \return Returns a string, previously set by setDebugName(); */ const c8* getDebugName() const { return DebugName; } protected: //! Sets the debug name of the object. /** The Debugname may only be set and changed by the object itself. This method should only be used in Debug mode. \param newName: New debug name to set. */ void setDebugName(const c8* newName) { DebugName = newName; } private: //! The debug name. const c8* DebugName; //! The reference counter. Mutable to do reference counting on const objects. mutable s32 ReferenceCounter; }; } // end namespace irr #endif这个接口十分的简洁,咱们仅仅要经过grab()和drop()这两个函数来进行操做就行。操做的规则在上面已经说的很清楚了。
对于在引擎中使用create方法创造出来的对象,通常来讲在create方面里面会调用new来构造一个对象。看这个接口的构造函数。咱们发现,进行构造以后引用计数就是1了。
假设咱们接着调用drop()函数。引用计数值就会变成0,从而自我进行销毁。也就是说,调用drop的次数要比调用grab的次数大1才可使引用计数值变为0,从而销毁自身。
这就是致使在上面,博主调试实验程序的时候,发现调用_pAnimator->drop()的时候,并无自我销毁的缘由。
在咱们将animator加入到node里面的时候,node中会保存一个这个对象的副本,从而调用一次grab()函数。
也就是。咱们需要再次的调用一次drop函数,才能够销毁掉这个animator。但是,博主这里并无这么作。
亲爱的读者们啊,也请你千万不要这么作。
咱们上面的所有创造过程,都是在程序主循环之上进行构造的。也就是说,在程序的主循环中还需要使用咱们建立的这个animator,从而才能够随着时间的进行,控制节点进行移动。
那么,就有一个问题了,这个对象究竟在什么地方进行终于的销毁了?
博主发现,在教程4的结尾处,当程序主循环结束以后,会调用一下_pDevice->drop()函数。是否是和这个对象的释放有关了?
咱们跟踪这个类的析构方法。依次获得例如如下的代码:
CIrrDeviceStub::~CIrrDeviceStub() { VideoModeList->drop(); FileSystem->drop(); if (GUIEnvironment) GUIEnvironment->drop(); if (VideoDriver) VideoDriver->drop(); if (SceneManager) SceneManager->drop(); if (InputReceivingSceneManager) InputReceivingSceneManager->drop(); if (CursorControl) CursorControl->drop(); if (Operator) Operator->drop(); if (Randomizer) Randomizer->drop(); CursorControl = 0; if (Timer) Timer->drop(); if (Logger->drop()) os::Printer::Logger = 0; }
用于实现那些所有设备都一样的部分。经过这个桩类在IrrlichtDevice这个接口与实际的CIrrDeviceWin32之间进行一个桥接。实现那些共同拥有的功能。在这个桩类析构函数中,发现会依次的调用各个子系统的drop函数,从而释放里面的资源。对于node来讲,它保存在SceneManager里面,继续跟踪,发现在引擎中仅仅有一个SceneManager。为CSceneManager。查看这个类的析构函数。获得例如如下的函数:
//! destructor CSceneManager::~CSceneManager() { clearDeletionList(); //! force to remove hardwareTextures from the driver //! because Scenes may hold internally data bounded to sceneNodes //! which may be destroyed twice if (Driver) Driver->removeAllHardwareBuffers(); if (FileSystem) FileSystem->drop(); if (CursorControl) CursorControl->drop(); if (CollisionManager) CollisionManager->drop(); if (GeometryCreator) GeometryCreator->drop(); if (GUIEnvironment) GUIEnvironment->drop(); u32 i; for (i=0; i<MeshLoaderList.size(); ++i) MeshLoaderList[i]->drop(); for (i=0; i<SceneLoaderList.size(); ++i) SceneLoaderList[i]->drop(); if (ActiveCamera) ActiveCamera->drop(); ActiveCamera = 0; if (MeshCache) MeshCache->drop(); for (i=0; i<SceneNodeFactoryList.size(); ++i) SceneNodeFactoryList[i]->drop(); for (i=0; i<SceneNodeAnimatorFactoryList.size(); ++i) SceneNodeAnimatorFactoryList[i]->drop(); if (LightManager) LightManager->drop(); // remove all nodes and animators before dropping the driver // as render targets may be destroyed twice removeAll(); removeAnimators(); if (Driver) Driver->drop(); }
//! Removes all animators from this scene node. /** The animators might also be deleted if no other grab exists for them. */ virtual void removeAnimators() { ISceneNodeAnimatorList::Iterator it = Animators.begin(); for (; it != Animators.end(); ++it) (*it)->drop(); Animators.clear(); }
在此次的追踪发现了。当咱们调用_pDevice->drop函数的时候。会依次的释放系统中所有的资源和对象。
但是这里,博主又有一个疑问?很是多对象仅仅有在这最后的步骤才会调用实际的析构函数进行销毁。那么也就是说,假设在系统的中途。咱们已经决定了再也不使用某个对象。并且保证在以后的过程当中都再也不使用了,假设仅仅是简单的调用一次drop是否是仍是会让这个对象依旧残留在系统里面,从而致使内存被占用。不需要的资源长期持有着了?Irrlicht引擎是否有某种机制可让咱们主动的释放掉一些资源了?查看源码发现,的确有这种一些方法。大部分都是经过remove开头的来实现这种功能。但是请注意,千万不要主动的调用两次drop()函数。
尽管这种确能够将对象提早销毁,但是违背了使用引言计数的初衷。在Irrlicht内部会出现野指针的状况。
毕竟你仍是需要和Irrlicht打交道的,这样作实在很差也不合理。
最适当的方法。就是经过remove方法来通知Irrlicht来释放掉你不想使用的资源。
现在给你们总结下,本篇文章的核心内容:
1.经过继承ISceneNodeAnimator来实现咱们本身的Animator
2.Irrlicht引用计数系统十分的简单,却很的有用。请你们依照它定义的规则来使用它。不然会出现意想不到的结果。
在博主本身进行分析Irrlicht所使用的各类框架技术以后,博主有股冲动,想要本身的来实现下或者说仿制下这些技术。毕竟。研究引擎。一方面是为了熟悉它,使用它。另一个更重要的缘由是为了学习这些技术,并把它据为己有。
因此。在从此,博主可能会另外在开一个系列。专门用来仿制引擎中的各类特性。试着实现一些仿真程序。
最后给出。本次试验程序的程序截图。十分的简单,一个Cube来回的移动而已:
此次的文章就到此结束了,感谢你们的关注。!
!