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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Ogre wiki 中级教程1 动画,点之间行走及四元数的基本应用

發(fā)布時間:2024/4/14 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Ogre wiki 中级教程1 动画,点之间行走及四元数的基本应用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

引言

在本教程中,我們將介紹如何讓一個實(shí)體以動畫的方式在預(yù)先定義的點(diǎn)之間行走。此外還將通過展示一個如何讓實(shí)體朝向它所移動的方向的例子來講解四元數(shù)的基本應(yīng)用。在創(chuàng)作這個demo的過程中你將慢慢把代碼添加到項(xiàng)目中,編譯并觀看結(jié)果。

Prerequisites (略)

Getting Started (略)

創(chuàng)建場景

開始之前,請注意我們在頭文件定義了三個變量。mEntity將保存我們創(chuàng)建的實(shí)體,mNode將保存我們創(chuàng)建的節(jié)點(diǎn),而mWalkList包含我們希望讓對象行走至之上的各個點(diǎn)。

查找ITutorial01::createScen函數(shù)并添加以下代碼。首先我們把環(huán)境光的各個分量設(shè)置為全滿,使我們能看到放置在場景中的對象。

// Set the default lighting.
???????? mSceneMgr->setAmbientLight(Ogre::ColourValue(1.0f, 1.0f, 1.0f));

接下來我們將在屏幕上創(chuàng)建一個機(jī)器人從而能操縱它。為完成這個任務(wù)我們將創(chuàng)建機(jī)器人對應(yīng)的實(shí)體,然后創(chuàng)建它所掛接的場景節(jié)點(diǎn)。

// Create the entity
??????? mEntity = mSceneMgr->createEntity("Robot", "robot.mesh");
?
??????? // Create the scene node
??????? mNode = mSceneMgr->getRootSceneNode()->
??????????? createChildSceneNode("RobotNode", Ogre::Vector3(0.0f, 0.0f, 25.0f));
??????? mNode->attachObject(mEntity);

這些看起來可能非常簡單,所以我將不解釋每一處的細(xì)節(jié)。下面一段代碼中,我們要告訴機(jī)器人它需要移動到哪里。如果你從未學(xué)習(xí)過STL,以下為你解釋deque的概念。deque對象是雙端對列的一個高效實(shí)現(xiàn),我們只需使用它的少數(shù)方法。push_front和push_back方法分別在隊(duì)頭和隊(duì)尾插入元素。front和back方法分別返回隊(duì)頭和隊(duì)尾的元素。pop_front和pop_back方法分別從隊(duì)頭和隊(duì)尾刪除元素。最后,empty方法返回該隊(duì)列是否為空。下列代碼添加兩個向量至deque,我們稍后將讓機(jī)器人在它們之間移動。

// Create the walking list
??????? mWalkList.push_back(Ogre::Vector3(550.0f,? 0.0f,? 50.0f ));
??????? mWalkList.push_back(Ogre::Vector3(-100.0f,? 0.0f, -200.0f));

接下來讓我們在場景中放置一些對象表示機(jī)器人將要經(jīng)過的點(diǎn)。這將讓我們看到機(jī)器人相對于屏幕上的對象在移動。注意它們所在位置的負(fù)Y分量,這把對象置于機(jī)器人將經(jīng)過的點(diǎn)的下方,當(dāng)機(jī)器人到達(dá)正確地點(diǎn)的時候?qū)⒄驹谏厦妗?br />
// Create objects so we can see movement
??????? Ogre::Entity *ent;
??????? Ogre::SceneNode *node;
?
??????? ent = mSceneMgr->createEntity("Knot1", "knot.mesh");
??????? node = mSceneMgr->getRootSceneNode()->createChildSceneNode("Knot1Node",
??????????? Ogre::Vector3(0.0f, -10.0f,? 25.0f));
??????? node->attachObject(ent);
??????? node->setScale(0.1f, 0.1f, 0.1f);
?
??????? ent = mSceneMgr->createEntity("Knot2", "knot.mesh");
??????? node = mSceneMgr->getRootSceneNode()->createChildSceneNode("Knot2Node",
??????????? Ogre::Vector3(550.0f, -10.0f,? 50.0f));
??????? node->attachObject(ent);
??????? node->setScale(0.1f, 0.1f, 0.1f);
?
??????? ent = mSceneMgr->createEntity("Knot3", "knot.mesh");
??????? node = mSceneMgr->getRootSceneNode()->createChildSceneNode("Knot3Node",
??????????? Ogre::Vector3(-100.0f, -10.0f,-200.0f));
??????? node->attachObject(ent);
??????? node->setScale(0.1f, 0.1f, 0.1f);

最后,我們?yōu)閿z像機(jī)設(shè)置一個良好的視點(diǎn)來觀察。我們將移動攝像機(jī)至一個較佳的位置:

// Set the camera to look at our handiwork
??????? mCamera->setPosition(90.0f, 280.0f, 535.0f);
??????? mCamera->pitch(Ogre::Degree(-30.0f));
??????? mCamera->yaw(Ogre::Degree(-15.0f));

現(xiàn)在編譯并運(yùn)行以上代碼。

動畫

我們現(xiàn)在要創(chuàng)建一些基本的動畫。在Orge中實(shí)現(xiàn)動畫非常簡單。你只需從實(shí)體對象獲取AnimationState,設(shè)置它的屬性,然后讓它可用。這將激活動畫,但是為了讓動畫能夠播放你需在各幀之后為動畫添加時間參數(shù)。我們將一步完成這些工作。首先,查找ITutorial01::createFrameListener函數(shù),并在調(diào)用BaseApplication::createFrameListener之后添加以下代碼:

// Set idle animation
??????? mAnimationState = mEntity->getAnimationState("Idle");
??????? mAnimationState->setLoop(true);
??????? mAnimationState->setEnabled(true);

第二行從實(shí)體中獲取AnimationState,第三行我們髙用setLoop( true )讓動畫一直循環(huán)。對于一些動畫(如死亡動畫),我們將其參數(shù)設(shè)為false。第四行真正讓動畫可用。但是等等...我們從哪里得來的"idle"?為何這個魔數(shù)會在這個地方?因?yàn)樗芯W(wǎng)格都擁有為它們所定義的動畫集合。為了看到你所使用的特定網(wǎng)格的所有動畫,你需要下載OgreMeshViewer以察看網(wǎng)格。

現(xiàn)在,如果我們編譯并運(yùn)行demo,我們看到...什么都沒有改變。這是因?yàn)槲覀冃枰诿恳粠脮r間變量更新動畫狀態(tài)。查找Tutorial01::frameRenderingQueued方法并添加這行代碼于函數(shù)的開始處:

mAnimationState->addTime(evt.timeSinceLastFrame);

現(xiàn)在編譯并運(yùn)行程序,你將看到機(jī)器人在原地表現(xiàn)空閑狀態(tài)的動畫。

移動機(jī)器人

現(xiàn)在我們開始著手來完成讓機(jī)器人在點(diǎn)和點(diǎn)之間移動這個棘手的任務(wù)。開始之前我要解釋一下我們定義好的變量。我們用4個變量來完成移動機(jī)器人的任務(wù)。首先我們用mDirection存儲機(jī)器人移動的方向,用mDestination存儲機(jī)器人將要到達(dá)的當(dāng)前目標(biāo)位置,用mDistance存儲機(jī)器人接下來要移動的距離。最后用mWalkSpeed存儲機(jī)器人的移動速度。


我們要做的第一件事是設(shè)置這些變量。我們將設(shè)置移動速度為每秒35個單位。有件重要的事情需要要注意:我們明確地設(shè)置mDirection為零向量,因?yàn)樯院笪覀儗⒂盟鼇泶_定機(jī)器人移動與否。添加以下代碼至ITutorial01::createFrameListener:

// Set default values for variables
???????? mWalkSpeed = 35.0f;
???????? mDirection = Ogre::Vector3::ZERO;


完成這些后,我們需要讓機(jī)器人運(yùn)動起來。我們簡單地改變動畫讓機(jī)器人移動。我們只想在有另一個可移動至之上的地點(diǎn)時才移動機(jī)器人。基于這個原因我們調(diào)用了ITutorial01::nextLocation function方法。在ITutorial01::frameRenderingQueued方法之上,調(diào)用AnimationState::addTime之前添加這段代碼:

if (mDirection == Ogre::Vector3::ZERO)
??????? {
??????????? if (nextLocation())
??????????? {
??????????????? // Set walking animation
??????????????? mAnimationState = mEntity->getAnimationState("Walk");
??????????????? mAnimationState->setLoop(true);
??????????????? mAnimationState->setEnabled(true);
??????????? }
??????? }

如果你編譯并運(yùn)行目前的代碼,這個機(jī)器人將在原地走動。這是因?yàn)闄C(jī)器人從值為零向量的方向開始并且ITutorial01::nextLocation始終返回true。下面的步驟我們將在ITutorial01::nextLocation方法中多添加一點(diǎn)智能。

現(xiàn)在我們要真正開始在場景中移動機(jī)器人,為此我們需要讓它在每幀移動一點(diǎn),找到ITutorial01::frameRenderingQueued方法,我們將在前面的if語言之后AnimationState::addTime調(diào)用之前添加下列代碼。這段代碼將處理機(jī)器人真正開始移動的情況;mDirection != Ogre::Vector3::ZERO
mWalkspeed乘以evt.timeSinceLastFrame的原因是為了保持走動速度恒定,即使幀率是變化的。


如果你只寫上Real move = mWalkspeed,機(jī)器人在速度較慢的機(jī)子上將會走得較慢,反之則走得較快。

else
???????? {
???????????? Ogre::Real move = mWalkSpeed * evt.timeSinceLastFrame;
???????????? mDistance -= move;

現(xiàn)在,我們需要檢測我們是否走過了目標(biāo)點(diǎn)。即如果現(xiàn)在mDistance小于0,我們需要跳回到那個點(diǎn)上,并設(shè)置移動到下一個點(diǎn)。請注意我們把mDirection設(shè)為零向量,如果nextLocation 方法沒有改變mDirection(即不需要再走向任何點(diǎn))則我們不再需要來回走動。

if (mDistance <= 0.0f)
???????????? {
???????????????? mNode->setPosition(mDestination);
???????????????? mDirection = Ogre::Vector3::ZERO;

我們已經(jīng)移動到一個點(diǎn),現(xiàn)在需要設(shè)置到下一個點(diǎn)的運(yùn)動。一旦我們知道是否需要移動到下一個點(diǎn),我們就可以設(shè)置適當(dāng)?shù)膭赢嫛H绻粋€點(diǎn)走動則設(shè)置走動動畫,如果沒有目標(biāo)點(diǎn)則設(shè)置空閑動畫。走完各個點(diǎn)設(shè)置空閑動畫是一件簡單的事情:

// Set animation based on if the robot has another point to walk to.
??????????????? if (! nextLocation())
??????????????? {
??????????????????? // Set Idle animation??????????????????? ?
??????????????????? mAnimationState = mEntity->getAnimationState("Idle");
??????????????????? mAnimationState->setLoop(true);
??????????????????? mAnimationState->setEnabled(true);
??????????????? }
??????????????? else
??????????????? {
??????????????????? // Rotation Code will go here later
??????????????? }
??????????? }


注意如果隊(duì)列里還有需要行走至其上的點(diǎn)我們就不再需要再次設(shè)置走動動畫。因?yàn)闄C(jī)器人已經(jīng)在走動了,沒有理由讓它再做一次。然而,如果機(jī)器人需要走向另一個點(diǎn)則我們需要旋轉(zhuǎn)它面向該點(diǎn)。現(xiàn)在我們在else分支留下一個站位注釋,記住稍后我們將回到這一處。

以上處理了當(dāng)我們離目標(biāo)地點(diǎn)非常近的情況。現(xiàn)在我們需要處理一般情況,即當(dāng)我們在走向該點(diǎn)的路上但還沒在那里。為完成這個任務(wù)我們在走動的方向上平移機(jī)器人,以move變量計算的單位數(shù)移動它,通過添加以下代碼來實(shí)現(xiàn):


???????????? {
???????????????? mNode->translate(mDirection * move);
???????????? } // else
???????? } // if

差不多完成了。我們的代碼做了所有的事情除了設(shè)置移動所需的變量。如果我們能正確設(shè)置移動變量我們的機(jī)器人將像設(shè)想的一樣移動。查找ITutorial01::nextLocation函數(shù),這個函數(shù)當(dāng)走完所有點(diǎn)的時候返回false。以下代碼將是這個函數(shù)的第一行,(注意你必須在函數(shù)的結(jié)尾處返回true)

if (mWalkList.empty())
???????????? return false;

現(xiàn)在我們需要設(shè)置變量(仍然在nextLocation method方法)。首先我們將目標(biāo)向量移出隊(duì)列,我們將通過目標(biāo)向量與場景節(jié)點(diǎn)的當(dāng)前位置相減來得到方向向量,但是這有一個問題。記起來我們在 frameRenderingQueued方法里將mDirection乘以移動的總量嗎?如果我們這么做,就需要方向向量是一個單位向量(即它的長度等于1)。這個規(guī)范化函數(shù)為我們做了這些,并返回向量原來的長度。這很方便,因?yàn)槲覀冞€需要設(shè)置到目的地的距離。

mDestination = mWalkList.front();? // this gets the front of the deque
??????? mWalkList.pop_front();???????????? // this removes the front of the deque
?
??????? mDirection = mDestination - mNode->getPosition();
??????? mDistance = mDirection.normalise();

現(xiàn)在編譯并運(yùn)行以上代碼,它能工作了!可以這樣說。機(jī)器人現(xiàn)在朝各個點(diǎn)行走,但是它一直面向Ogre::Vector3::UNIT_X方向(它的默認(rèn)方向)。當(dāng)它朝著目標(biāo)點(diǎn)移動的時候我們將改變它所面向的方向。

我們需要做的是獲取機(jī)器人的朝向,然后使用旋轉(zhuǎn)函數(shù)將對象旋轉(zhuǎn)至正確的位置。插入以下代碼于我們之前留下的站位注釋。第一行獲取機(jī)器人面向的方向,第二行創(chuàng)建一個四元數(shù)表示從當(dāng)前方向至目標(biāo)方向的旋轉(zhuǎn)。第三行真正旋轉(zhuǎn)了機(jī)器人。

Ogre::Vector3 src = mNode->getOrientation() * Ogre::Vector3::UNIT_X;
???????? Ogre::Quaternion quat = src.getRotationTo(mDirection);
???????? mNode->rotate(quat);

我們在基本教程4簡單提及了四元數(shù),但這是第一次使用。簡單說來,四元數(shù)表示在3D空間里的旋轉(zhuǎn)。它們被用來跟蹤對象如何在空間中定位,且可以用于在Ogre中旋轉(zhuǎn)對象。第一行我們調(diào)用getOrientation方法,它返回一個表示機(jī)器人在空間中的朝向的四元數(shù),因?yàn)镺rge無法確定哪一面才是機(jī)器人的正面,我們必須將朝向乘以UNIT_X向量(這是機(jī)器人"天生"的朝向)來得到機(jī)器人的當(dāng)前朝向。我們將這個方向存儲于src變量。第二行g(shù)etRotationTo 方法返回一個四元數(shù)表示從機(jī)器人朝向的方向至我們希望它面向的方向的的旋轉(zhuǎn)。第三行我們旋轉(zhuǎn)節(jié)點(diǎn)讓它面向一個新的方向。

我們編寫的所有代碼中只有一個問題,有一種情況會使SceneNode::rotate方法失敗,如果我們嘗試讓機(jī)器人做180度旋轉(zhuǎn),執(zhí)行旋轉(zhuǎn)的代碼將會因?yàn)槌沐e誤而崩潰。為了修正它,我們需要測試是否執(zhí)行了180旋轉(zhuǎn)。如果是這樣,我們將簡單地讓機(jī)器人偏航180度以替代旋轉(zhuǎn)。為此我們刪除我們剛放進(jìn)去的三行代碼并以下列代碼替代:

Ogre::Vector3 src = mNode->getOrientation() * Ogre::Vector3::UNIT_X;
??????? if ((1.0f + src.dotProduct(mDirection)) < 0.0001f)
??????? {
??????????? mNode->yaw(Ogre::Degree(180));
??????? }
??????? else
??????? {
??????????? Ogre::Quaternion quat = src.getRotationTo(mDirection);
??????????? mNode->rotate(quat);
??????? } // else

這些代碼除了if語句包含的東西以外都是不言自明的。如果兩個單位向量彼此相反(即它們之間的夾角為180度),則它們的點(diǎn)積將為-1。所以如果我們求出兩個向量的點(diǎn)積并測試其結(jié)果等于-1,則需要偏航180度,除此之外我們用旋轉(zhuǎn)來替代。為什么我加上1.0f并測試是否小于0.0001f?別忘了浮點(diǎn)舍入誤差。你永遠(yuǎn)不要直接比較兩個浮點(diǎn)數(shù)。最后,你至少需要懂一點(diǎn)圖形程序所需要的線性代數(shù)知識!至少,你應(yīng)該復(fù)習(xí)一下四元數(shù)與旋轉(zhuǎn)入門教程,并閱讀一些關(guān)于基本的向量和距陣運(yùn)算的書籍。

現(xiàn)在我們的代碼寫好了,編譯并運(yùn)行這個demo可以看到機(jī)器人在給定的各個點(diǎn)之間走動。

轉(zhuǎn)載于:https://www.cnblogs.com/quasimodo/archive/2012/08/24/2653762.html

總結(jié)

以上是生活随笔為你收集整理的Ogre wiki 中级教程1 动画,点之间行走及四元数的基本应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。