本教程演示了如何把一个引擎加载雷神之锤3地图进引擎,创建一个用来优化渲染速度的SceneNode和如何创建一个用户控制的摄像机。请注意,在开始本教程之前你应该先了解该引擎的基础知识,如果你还没有了解的话,请先查看第一个教程,1.HelloWorld。
这个例子的结果会是这样的:
让我们开始吧 |
首先让我们像helloworld的例子一样:我们包含Irrlicht的头文件和一个额外的文件以便能够使用控制台的一些程序类。
正如已经在HelloWorld例子写的,在Irrlicht引擎中所有类都包含在命名空间"IRR"中。为了不在每个类名前加 irr:: ,我们在开始处告诉编译器包含irr命名空间。
在这里面已包含了'core', 'scene', 'video', 'io' 和 'gui'五个子命名空间,所以我们不
需要像helloworld一样包含这五个命名空间,当然如果你喜欢也可以像之前例子一样。就像以下代码:
using namespace irr; |
接着,为了使我们的程序可以使用Irrlicht的DLL文件,我们需要连接Irrlicht.lib库。我们可以在项目设置中设置此选项,但我们使用pragma comment 预处理将会更加简单:
#pragma comment(lib, "Irrlicht.lib") |
好了,接下来我们使用main() 方法作为开始,这里不使用WinMain(),因为main更精短些。
int main() { |
像HelloWorld例子中一样,我们用createDevice()创建一个IrrlichtDevice。现在不同的是,我们要求用户选择使用硬件加速驱动。当然软件设备也可以画一个巨大的“雷神之锤3:地图但它太慢。不过这里为了学习,我们提供了可以自己选择的方法。
// 要求用户选择驱动程序 video::E_DRIVER_TYPE driverType; printf("Please select the driver you want for this example:\n"\ " (a) OpenGL 1.5\n (b) Direct3D 9.0c\n (c) Direct3D 8.1\n"\ " (d) Burning's Software Renderer\n (e) Software Renderer\n"\ " (f) NullDevice\n (otherKey) exit\n\n"); char i; std::cin >> i; switch(i) { case 'a': driverType = video::EDT_OPENGL; break; case 'b': driverType = video::EDT_DIRECT3D9;break; case 'c': driverType = video::EDT_DIRECT3D8;break; case 'd': driverType = video::EDT_BURNINGSVIDEO;break; case 'e': driverType = video::EDT_SOFTWARE; break; case 'f': driverType = video::EDT_NULL; break; default: return 1; } // 创建设备和如果创建失败退出 IrrlichtDevice *device = createDevice(driverType, core::dimension2d<u32>(640, 480)); if (device == 0) return 1;
获取指向视频驱动和场景管理的指针,这样我们就不需要总是写device->getVideoDriver() 和device->getSceneManager()。
video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); |
要显示雷神之锤3地图,我们首先需要加载它。雷神之锤3地图被打包成.pk3格式文件,你可以看成像.zip文件打包一样。
现在我们把.pk3文件加入我们的文件系统,加入之后,我们就能够像存储在硬盘上一样从打包文件中读取到地图文件。
device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3"); |
现在,我们可以调用getMesh()加载网格。我们返回一个IAnimatedMesh指针。如你所知,雷神之锤3:地图不是真正的动画,它们只是一些附加材质的静态几何块。
因此,IAnimated的网格组成,只有一帧。所以我们将通过quake level来获取“动画”的“第一帧”并且使用addOctTreeSceneNode()创建一个OctTree 的场景节点。
OctTree(八叉树)通过只绘制目前可见的几何形状来优化场景。如果这里使用AnimatedMeshSceneNode替换OctTree 的话,引擎将没有任何优化的绘制完整的网格几何。
试试看:将addOctTreeSceneNode 替换成addAnimatedMeshSceneNode ,然后通过视频驱动比较一下两者(这里可以通过IVideoDriver 类的一个方法 getPrimitiveCountDrawed() 来做到),请注意Octree 只有当绘制由很多几何体组成巨大网格时才能起到作用。
scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp"); scene::ISceneNode* node = 0; if (mesh) node = smgr->addOctTreeSceneNode(mesh->getMesh(0)); |
由于该网格不是以(0,0,0)为原点的,我们这里把它转换一下。
if (node) node->setPosition(core::vector3df(-1300,-144,-1249)); |
现在我们只需要一个摄像头用来看雷神之锤3的地图。我们要创建一个用户控制的摄像机。Irrlicht引擎提供了不同种类的。例如Maya摄像头,按下鼠标左键可以控制相机旋转,按下两个按钮时放大,按下鼠标右键移动。这个可以对通过addCameraSceneNodeMaya()创建。但在这个例子中,我们要创建一个像在第一人称射击游戏(FPS)中的摄像头行为:
smgr->addCameraSceneNodeFPS(); |
鼠标光标需要隐藏,所以这里我们把它设置成不可见。
device->getCursorControl()->setVisible(false); |
好了,我们完成了所需的所有事情,现在我们要把它画出来。然后我们把每一帧的FPS信息画在窗口标题上。这里'if (device->isWindowActive())' 是可选的,不过当为了防止当其它程序被激活切换任务时,引擎渲染改变鼠标光标的位置是就要加上了。
int lastFPS = -1; while(device->run()) { if (device->isWindowActive()) { driver->beginScene(true, true, video::SColor(255,200,200,200)); smgr->drawAll(); driver->endScene(); int fps = driver->getFPS(); if (lastFPS != fps) { core::stringw str = L"Irrlicht Engine - Quake 3 Map example ["; str += driver->getName(); str += "] FPS:"; str += fps; device->setWindowCaption(str.c_str()); lastFPS = fps; } } else device->yield(); //irr::IrrlichtDevice::yield()将避免当窗口不活跃时,繁忙的循环,吃掉所有CPU周期。 }
最后,删除Irrlicht的设备。
device->drop(); return 0; } |
就是这样。编译并测试一下这个程序吧。