OGRE教程(一):SceneNode, Entity, SceneManager

 
 
原始作者: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 你的想法?

  如果你對文中的一些東西不是很清楚,請到論壇中討論。關於這篇譯稿的問題,請發郵件給我 [email protected] 我的blog爲:http://akinggame.gameres.com 

發佈了91 篇原創文章 · 獲贊 5 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章