原始作者:Clay Culver 中文譯者:Antking OGRE教程(一) OGRE教程(二) 注意:這篇文章是針對OGRE 1.0.0,如果你使用其他的版本,如果遇到問題請到論壇中討論。 目錄 1 讀者對象 2 簡介 3 如何開始 4 OGRE如何工作 4.1 SceneManager 基礎 4.2 Entity 基礎 4.3 SceneNode 基礎 5 你的第一個OGRE程式 6 座標和向量 7 添加另一個物體 8 Entities more in Depth 9 SceneNodes more in Depth 10 嘗試 10.1 Scale(變換) 10.2 旋轉 11 總結 12 你的想法? 1 讀者對象
這篇文章是假設你有C++編程知識,並設定了OGRE在編譯器中,(如果你不知道如何設定,請參看《OGRE初學者引導》),對OGRE一無所知的情況下。 2 簡介 在這篇教程中,我將介紹一些基本的OGRE結構:SceneManager, SceneNode, and Entity 。在這篇文章中,我不會使用太多的代碼,而是講解一些基本的理論。 通過這篇文章,你將慢慢的添加代碼到你的程式中,並觀察他的運行結果。對於這些理論,並沒有固定的代碼,你也可以通過這些理論寫出其他的代碼。 3 如何開始 對於這篇教程,我們使用了一段固定的代碼,(也許你在《OGRE初學者引導》見過)。在這段代碼中,你可以忽視其他的代碼,但createScene 中的代碼應注意。在下一篇教程中,我們將深入講解OGRE是如何工作的,因此這裡的基本知識很重要。添加下面的代碼到你的編譯器中: #include "ExampleApplication.h" class TutorialApplication : public ExampleApplication { protected: public: TutorialApplication() { } ~TutorialApplication()
{
} protected:
void createScene(void)
{
} }; #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) #else int main(int argc, char **argv) #endif {
// Create application object
TutorialApplication app;
try {
app.go();
} catch( Exception& e ) { #if OGRE_PLATFORM == PLATFORM_WIN32
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_IConERROR | MB_TASKMODAL); #else
fprintf(stderr, "An exception has occured: %s/n", e.getFullDescription().c_str()); #endif
}
return 0; }
如果你是使用WINDOWS下的OGRESDK,請確定添加"[OgreSDK_DIRECTORY]/samples/include"這個目錄到 這個項目中。如果是使用OGRE的原始碼,請添加"[OgreSource_DIRECTORY]/Samples/Common/include"這個 目錄。然後,就可以編譯和運行了,如果還遇到問題,請參看WIKI的有關這些資訊的頁面,如果任然不行,請到論壇中討論。 程式控制:用WASD移動,滑鼠確定方向。ESC鍵退出。 4 OGRE如何工作 一個很寬的主題。我們將從他的基礎 The SceneNode, Entity, and SceneManager 講解。 4.1 SceneManager 基礎 SceneManager管理出現在螢幕上的所有物體。當你把一個物體放到情境中,SceneManager就將對這個物體的座標進行跟蹤。當你建立 一個攝象機時,SceneManager就將對他們所有東西進行跟蹤。當你建立木塊,廣告牌,燈光...,SceneManager 還是會對他們進行跟蹤。 SceneManager也有許多的類型,有渲染地形的,有渲染BSP樹的,等等。在這篇文章中,你將學到許多類型的SceneManager。 4.2 Entity 基礎 Entity是你在情境中渲染的物體的形狀。你能把他想象成3D網格。一個機器人有網格,一條魚有網格,你的角色行走的地形有一個大的網格。而如燈光,廣告牌(Billboards),粒子,攝象機等沒有Entity。 值得注意的一件事是,OGRE是根據物體的座標和方向的可渲染性分別進行渲染的。這就意味著你不能直接到情境中的一個網格進行渲染。你必須將要渲染的物體的網格給予SceneNode ,SceneNode 包含諸如座標和方向等資訊。 4.3 SceneNode 基礎 在上面已經提到,SceneNode 用於保持對所有與它聯絡的物體的座標和方向進行跟蹤。當你建立一個網格,他並不會在情境中進行渲染,除非你將這個網格賦予SceneNode 。相似的,SceneNode 不是你要在螢幕上顯示的物體,只有當你建立一個SceneNode,並將一個網格賦予他,他才會在螢幕上顯示一個物體。 SceneNode 能將許多的物體賦予他。例如,你在螢幕上有一個行走的物體,並且你想產生一個燈光環繞著他。首先,你需要建立一個SceneNode ,然後建立角色的網格,並將他賦予SceneNode 。下一步建立燈光,並將他賦予SceneNode 。SceneNode也允許你將他賦予其他SceneNode,這樣就建立了一個有等級的節點系統。在下一篇文章中,我們將更詳細的講解SceneNode的功能。 一個重要的概念是,SceneNode的位置總是和他的父SceneNode有關,並且SceneManager包含所有被賦值的SceneNodes的根節點。 5 你的第一個OGRE程式 現在回到我們開始建立的代碼中,找到TutorialApplication::createScene 成員函數。在這篇教程中,我們只將對這個函數進行操作。我們要做的第一件事是建立網格。我們可以通過調用 SceneManager's createEntity 函數來實現。添加下面的行到createScene : Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" ); 到這裡,有幾個問題將彈出。首先,mSceneMgr來自哪裡,我們用什麼值去調用這個函數? mSceneMgr常量包含當前SceneManager物體(這是通過ExampleApplication類來實現的)。第一個參數是我們通過 createEntity建立的網格的名字。所有的網格必須有唯一的名字。如果你試圖建立兩個有相同名字的網格,你將得到錯誤。“robot.mesh" 唯一表明了我們要使用的網格的名字。在這裡,我們使用的網格是通過ExampleApplication類匯入的。 到現在,我們建立了網格,我們還需要用SceneNode 與他關聯。又因為每個SceneManager有一個根 SceneNode,我們將用下面的代碼建立他的一個子節點: SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" ); 這條長長的語句首先調用當前SceneManager的getRootSceneNode方法。然後,他又調用根SceneNode的 createChildSceneNode方法。createChildSceneNode方法中的參數是我們建立的SceneNode的名字。像前面所 述一樣,SceneNode也不允許名字相同。 最後,我們需要將網格賦予SceneNode,以便於ROBOT有渲染的座標: node1->attachObject( ent1 ); 一切OK!編譯並運行,你將在螢幕上看到一個機器人。 6 座標和向量 在我們開始講解之前,我們有必要討論一下螢幕座標和OGRE向量。OGRE像其他的圖象引擎一樣,用X,Z軸表示水平的面,Y表示垂直的軸。正如你看 螢幕一樣,X軸表示你的螢幕的左,右,右面是X軸正方向。Y軸表示你的螢幕的下到上,上面是Y軸正方向。Z軸表示你的螢幕的由裡到外,外面是Z軸正方向。 注意,我們的機器人如何面對X軸的正方向?這是網格的一個屬性,他是如何設計的呢?OGRE並沒有規定你的初始模型的方向,所以你匯入的每個物體網格的方向都是不一定的。 OGRE用向量類來表示座標和方向。這些向量vectors可以定義為2(Vector2),3(Vector3),4(Vector4)維,其中Vector3最常用,如果你對向量不熟悉,我建議你看一下下面的網站: http://en.wikipedia.org/wiki/Vector_%28spatial%29
筆者註:我建議大家在學習3D編程知識之前,看一下《線形代數》,《空間解析幾何》。 關於向量的數學知識在以後的學習中將發揮重要的作用。 7 添加另一個物體
前面,我們講解了座標系統的作用,下面我們回到我們的代碼中來。在前面,我們添加的代碼中,我們並沒有明確表明我們的機器人出現的座標。但在OGRE中,有許多的函數可以初始座標。例如: SceneNode::createChildSceneNode 成員函數中,有三個參數:SceneNode的名字,SceneNode的座標,和SceneNode的基本旋轉。對於座標,正如你所看到的,我們初始時為(0,0,0)。現在,我們建立另一個SceneNode,但是這次我們表明他的座標為另一個值: Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" ); SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) ); node2->attachObject( ent2 ); 這看起來和前面我們定義的一樣,只是有輕微的變化。首先,我們命名網格和SceneNode時,有很小的不同。第二件不同的是我們初始網格開始的位置 偏離了根SceneNode50個單位(記住SceneNode所有的座標和他們的父節點有關)。編譯並運行,現在,你有兩個機器人。 8 Entities more in Depth(深入Entities) Entities類功能非常強大,我在這裡並不想覆蓋的太寬,只講一下他的基本。首先,我們不得不提一下Entities中的一些功能強大的成員函數。 第一個是Entity::setVisible 和Entity::isVisible.你能把任何Entity 設定成可視。如果你需要先隱藏Entity ,然後又顯示它,你可以不調用這個函數,而採用先刪除Entity ,顯示時又從建立立他。物體的網格和文理自動拷貝到記憶體中,不需要你自己儲存他們。你需要儲存的是Entity物體的建立和刪除的東西。 getName函數用於返回Entity的名字,getParentSceneNode函數用於返回與Entity相關的SceneNode。 9 SceneNodes more in Depth SceneNode 類非常複雜。在SceneNodes 上有許多的事情可以做,因此,我們在這裡只覆蓋到一些通用的功能。 你能用SceneNode的getPosition,setPosition函數得到和設定SceneNode的點(通常和SceneNode的父節點有關)。你能用translate函數移動物體。 SceneNode不僅能設定位置,還能管理物體的變換大小和旋轉。你能設定變換大小用scale函數。你能用yaw,roll,pitch旋轉物 體。你也能用resetOrientation函數設定物體的旋轉參數。你還能用 setOrientation,getOrientation,rotate函數實現高品質的旋轉。 我們再來看一下attachObject函數,如果你想把物體繫到SceneNode點上,這些相關函數將非常有 用:numAttachedObjects,getAttachedObject(這個函數有多個版本),detatchObject(也有多個版 本),detatchAllObjects.也有整個一組函數處理父和子SceneNode。 所有的座標和父SceneNode節點相關,我們能做兩個相互移動的SceneNode節點。下面有一段代碼: Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" ); SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" ); node1->attachObject( ent1 ); Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" ); SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) ); node2->attachObject( ent2 ); 如果我們把第六行: SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) ); 變化如下: SceneNode *node2 = node1->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) ); 然後把RobotNode2 作為RobotNode的子節點。如果你移動node1就將移動node2.例如,下面的代碼移動RobotNode2 : node2->translate( Vector3( 10, 0, 10 ) ); 下面的代碼可以移動RobotNode,因為RobotNode2是RobotNode的子節點,RobotNode2也將跟隨移動: node1->translate( Vector3( 25, 0, 0 ) ); 如果,你在這裡有麻煩,我們可以理解成從根SceneNode 自頂向下。在這裡,我們開始node1從(0,0,0),然後轉移到(25,0,0),因此,node1的座標為(25,0,0)。node2開始在(50,0,0),加上(10,0,0)。因此新座標為(60,0,10)。 現在,讓我們來計算這些物體的真正座標。從根SceneNode 節點開始,他的位置總是(0,0,0)。現在,node1的座標為(root+node1):(0,0,0)+(25,0,0)=(25, 0, 0). 而node2是node1子節點,因此,他的座標為(root + node1 + node2): (0, 0, 0) + (25, 0, 0) + (60, 0, 10) = (85, 0, 10). 這隻是描述關於SceneNode座標層次的一個例子。 你能通過getSceneNode,getEntity函數得到SceneNodes and Entities 的名字。這兩個函數是SceneManager中的方法,因此,你不需要在你建立的每個SceneNode中保持一個指標。你只需要懸掛你經常用的那個。 10 嘗試 通過這章的學習,我們學習了Entities, SceneNodes, and the SceneManager. 的基本知識。下面,我將給出他們的一些常式。在這個例子中,我們在情境中建立一組機器人。 Scale 下面的代碼是通過SceneNode中的scale函數旋轉網格。 Entity *ent = mSceneMgr->createEntity( "Robot", "robot.mesh" ); SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" ); node->attachObject( ent ); node->scale( .5, 1, 2 ); ent = mSceneMgr->createEntity( "Robot2", "robot.mesh" ); node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) ); node->attachObject( ent ); node->scale( 1, 2, 1 ); 旋轉 下面的代碼是通過角度和半徑,用the yaw, pitch, and roll 函數旋轉物體。 Entity *ent = mSceneMgr->createEntity( "Robot", "robot.mesh" ); SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode", Vector3( -100, 0, 0 ) ); node->attachObject( ent ); node->yaw( Degree( -90 ) ); ent = mSceneMgr->createEntity( "Robot2", "robot.mesh" ); node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2"); node->attachObject( ent ); node->pitch( Degree( -90 ) ); ent = mSceneMgr->createEntity( "Robot3", "robot.mesh" ); node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode3", Vector3( 100, 0, 0 ) ); node->attachObject( ent ); node->roll( Degree( -90 ) ); 11 總結 在一章中,我們學習了一些關於SceneManager, SceneNode, and Entity 類的基本知識。但對文中提到的函數,不是很瞭解,在下一章中,我們將詳細講解這些函數。 12 你的想法? 如果你對文中的一些東西不是很清楚,請到論壇中討論。關於這篇譯稿的問題,請發郵件給我 antking@gmail.cn 我的blog為:http://akinggame.gameres.com |