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

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 你的想法?

  如果你对文中的一些东西不是很清楚,请到论坛中讨论。关于这篇译稿的问题,请发邮件给我 antking@gmail.cn 我的blog为:http://akinggame.gameres.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值