日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Irrlicht 3D Engine 笔记系列 之 自己定义Animator

發布時間:2024/8/22 编程问答 49 如意码农
生活随笔 收集整理的這篇文章主要介紹了 Irrlicht 3D Engine 笔记系列 之 自己定义Animator 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者: i_dovelemon

日期: 2014 / 12 / 17

來源: CSDN

主題: Custom Animator, Referenced count

引言

在昨天的文章《Irrlicht 3D Engine 筆記系列 之 教程4 - Movement》中,博主向大家保證會在今天向大家實際操作怎樣擴展Irrlicht引擎的Animator特性。假設讀者對Irrlicht的Animator的特性不是非常了解的話,請先了解下前面的那篇文章,本片文章是在上次文章的基礎上進行的。

Custom Animator

經過昨天我們的分析,發現僅僅要我們繼承ISceneNodeAnimator這個接口來實現我們自己的Animator就能夠了。所以以下,我們就按著這個思路來對創建我們自己定義的Animator。

首先,對我們將要創建的Animator進行下簡單的描寫敘述:

博主想要創建一個沿著某條直線進行循環運動,而且物體本身在旋轉的Animator。博主稱之為Roll Animator(翻滾動畫)。

好了。在明白了我們想要創建的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

事實上實現。也十分的簡單。最重要的控制方案是在animateNode這個函數里面實現的。對于這個函數,我們須要知道一點,它的參數列表中的第二個參數的意義并非類似cocos2d-x里面的調用該函數的時間間隔。在Irrlicht內部維持了一個時間計數器,這個計數器從系統啟動開始就開始計時。這里的timeMs就是系統此時的時間,以MS計算。

所以讀者大概。就行明確博主為什么要在類內部定義的當前時間了。我們通過這個所謂的當前時間來與系統實際的當前時間進行比較,計算出時間間隔,從而控制節點進行移動。

想要實現自己的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

Referenced Counter

博主在編寫這個實驗程序的時候。發現當我們調用_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;
}

CIrrDeviceStub是Irrlicht引擎中全部IrrlichtDevice設備的共同擁有的一個樁類。

用于實現那些全部設備都同樣的部分。通過這個樁類在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();
}

在這個析構函數中,能夠發現有一個名為removeAnimators()的函數,我們繼續跟蹤,例如以下所看到的:

		//! 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();
}

哈哈,在這里,調用了一次drop(),從而實現了對Animator對象的銷毀。

在這次的追蹤發現了。當我們調用_pDevice->drop函數的時候。會依次的釋放系統中全部的資源和對象。

可是這里,博主又有一個疑問?非常多對象僅僅有在這最后的步驟才會調用實際的析構函數進行銷毀。那么也就是說,假設在系統的中途。我們已經決定了不再使用某個對象。而且保證在之后的過程中都不再使用了,假設僅僅是簡單的調用一次drop是不是還是會讓這個對象依舊殘留在系統里面,從而導致內存被占用。不須要的資源長期持有著了?Irrlicht引擎是否有某種機制可以讓我們主動的釋放掉一些資源了?查看源碼發現,的確有這種一些方法。大部分都是通過remove開頭的來實現這種功能??墒钦堊⒁?,千萬不要主動的調用兩次drop()函數。

盡管這種確可以將對象提前銷毀,可是違背了使用引言計數的初衷。在Irrlicht內部會出現野指針的情況。

畢竟你還是須要和Irrlicht打交道的,這樣做實在不好也不合理。

最適當的方法。就是通過remove方法來通知Irrlicht來釋放掉你不想使用的資源。

如今給大家總結下,本篇文章的核心內容:

1.通過繼承ISceneNodeAnimator來實現我們自己的Animator

2.Irrlicht引用計數系統十分的簡單,卻很的有用。請大家依照它定義的規則來使用它。否則會出現意想不到的結果。

在博主自己進行分析Irrlicht所使用的各種框架技術之后,博主有股沖動,想要自己的來實現下或者說仿制下這些技術。畢竟。研究引擎。一方面是為了熟悉它,使用它。另外一個更重要的原因是為了學習這些技術,并把它據為己有。

所以。在今后,博主可能會另外在開一個系列。專門用來仿制引擎中的各種特性。試著實現一些仿真程序。

最后給出。本次試驗程序的程序截圖。十分的簡單,一個Cube來回的移動而已:

這次的文章就到此結束了,感謝大家的關注。!

!

總結

以上是生活随笔為你收集整理的Irrlicht 3D Engine 笔记系列 之 自己定义Animator的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。