这个例子将简单的告诉我们如何使用Irr引擎的地形渲染,以及如何使用地形渲染三角形选择器进行摄象机与地形之间碰撞检测。

/*

这个例子将简单的告诉我们如何使用Irr引擎的地形渲染,以及如何使用地形渲染三角形选择器进行摄象机与地形之间碰撞检测。

请注意,Irr引擎中的地形渲染是基于Spintz先生的GeoMipMapSceneNode,非常感谢他。

我们将使用小型的高度图来生成大型的地图场景,所以我们不得不使用---LOD技术。

这个例子的开头依旧没什么特殊的,我们包含Irr头文件,设置好命名空间,库链接后,再设置一个事件接收器来监听用户输入,如果用户按下“W”键,则地形切换成线型网状,若用户按下“D”键,则在标准绘制和细节绘制之间进行切换。

(译者注:EMT_SOLID,最基本的标准绘制,即简单的将纹理贴在模型上。EMT_DETAIL_MAP,是双层纹理的渲染模式,它将第一层纹理简单贴在模型上之后,将第二层纹理进行放大缩小后混合于第一层之上,以达到更好的渲染效果,这种模式通常在大型地形渲染时使用)
*/
#include <irrlicht.h>
#include <iostream>

using namespace irr;

#pragma comment(lib, "Irrlicht.lib")


class MyEventReceiver : public IEventReceiver
{
public:

MyEventReceiver(scene::ISceneNode* terrain)
{
   // 为了方便改变地形渲染模式,我们保存一下地形场景节点的指针
   Terrain = terrain;
}

bool OnEvent(SEvent event)
{
   // 如果用户按下W或者D键
   if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
   {
    switch (event.KeyInput.Key)
    {
    case irr::KEY_KEY_W:
     // 切换成线状地形渲染
     Terrain->setMaterialFlag(video::EMF_WIREFRAME, !Terrain->getMaterial(0).Wireframe);
     Terrain->setMaterialFlag(video::EMF_POINTCLOUD, false);
     return true;
    case irr::KEY_KEY_P:
     // 切换为点状地形渲染
     Terrain->setMaterialFlag(video::EMF_POINTCLOUD, !Terrain->getMaterial(0).PointCloud);
     Terrain->setMaterialFlag(video::EMF_WIREFRAME, false);
     return true;
    case irr::KEY_KEY_D:
     // 普通标准绘制和细节绘制中进行切换
     Terrain->setMaterialType(
      Terrain->getMaterial(0).MaterialType == video::EMT_SOLID ?
      video::EMT_DETAIL_MAP : video::EMT_SOLID);
     return true;
    }
   }

   return false;
}

private:
scene::ISceneNode* Terrain;
};


/*
类似于其他例子一样的主函数。
*/
int main()
{
// 让用户选择设备类型

video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;

printf("Please select the driver you want for this example:\n"\
   " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
   " (d) Software Renderer\n (e) Burning's Software Renderer\n"\
   " (f) NullDevice\n (otherKey) exit\n\n");

char i;
std::cin >> i;

switch(i)
{
   case 'a': driverType = video::EDT_DIRECT3D9;break;
   case 'b': driverType = video::EDT_DIRECT3D8;break;
   case 'c': driverType = video::EDT_OPENGL;   break;
   case 'd': driverType = video::EDT_SOFTWARE; break;
   case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
   case 'f': driverType = video::EDT_NULL;     break;
   default: return 1;
}

// 创建设备

IrrlichtDevice* device = createDevice(driverType, core::dimension2d<s32>(640, 480));

if (device == 0)
   return 1;


/*
首先,我们为场景增加一些标准元素:一个Irr引擎的Logo,一个小的帮助界面,
一个用户控制摄象机,并且屏蔽鼠标图标。
*/  

video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* env = device->getGUIEnvironment();

driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);

// 增加一个Logo
env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
   core::position2d<s32>(10,10));

// 更换一个字体
env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));

// 增加一个帮助界面
gui::IGUIStaticText* text = env->addStaticText(
   L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map",
   core::rect<s32>(10,440,250,475), true, true, 0, -1, true);

// 增加一个摄象机
scene::ICameraSceneNode* camera =
   smgr->addCameraSceneNodeFPS(0,100.0f,1200.f);

camera->setPosition(core::vector3df(1900*2,255*2,3700*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(12000.0f);

// 屏蔽鼠标图标
device->getCursorControl()->setVisible(false);

/*
现在开始,我们开始渲染地形场景节点:我们和创建别的场景节点类似,
使用ISceneManager::addTerrainSceneNode()来创建它。第一个参数是我们使用的高度图的
文件名称。高度图实际上就是一个简单的灰度纹理图,地形渲染器通过它创建3D地形。
为了使用地形看起来更大一些,我们创建时对其进行放大。
又因为我们场景中暂时不需要灯光,所以我们关闭了灯光。
之后我们设置terrain-texture.jpg作为地图的第一层纹理,而detailmap3.jpg做为第二层纹理:
最后,我们保持第一层纹理大小不变,第二层纹理放大了20倍才进行渲染。
*/

// 增加一个场景节点
scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
   "../../media/terrain-heightmap.bmp",
   0,           // 父节点
   -1,           // 节点ID编号
   core::vector3df(0.f, 0.f, 0.f),    // 节点坐标
   core::vector3df(0.f, 0.f, 0.f),    // 旋转矩阵
   core::vector3df(40.f, 4.4f, 40.f),   // 缩放矩阵
   video::SColor ( 255, 255, 255, 255 ), // 顶点颜色
   5,           // LOD最大程度
   scene::ETPS_17,        // 地形块大小
   4           // 平滑度
   );
// 译者注;地形块大小是与细节地形渲染紧密相关的,详情请参看引擎源码中的ETerrainElements.h文件。

terrain->setMaterialFlag(video::EMF_LIGHTING, false);

terrain->setMaterialTexture(0, driver->getTexture("../../media/terrain-texture.jpg"));
terrain->setMaterialTexture(1, driver->getTexture("../../media/detailmap3.jpg"));

terrain->setMaterialType(video::EMT_DETAIL_MAP);

terrain->scaleTexture(1.0f, 20.0f);

/*
为了能与地形进行碰撞检测,我们需要创建一个三角碰撞检测器。
如果你想知道碰撞检测器如何运作,请看碰撞检测的例子。
我们创建了一个摄象机,并为它创建了一个碰撞器,这样,我们的摄象机与地形之间
就会有碰撞处理,摄象机于是就不会移动出地表了。
*/

// 为地形创建一个三角型碰撞检测
scene::ITriangleSelector* selector
   = smgr->createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
selector->drop();

// 为摄象机捆绑一个碰撞检测器
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
   selector, camera, core::vector3df(60,100,60),
   core::vector3df(0,0,0),
   core::vector3df(0,50,0));
camera->addAnimator(anim);
anim->drop();

/*
为了使用户能够在不同的模式之间进行切换,我们创建了一个事件接收器的实例,
另外我们如其他例子一样创建了一个天空盒。
*/

// 创建一个事件接收器
MyEventReceiver receiver(terrain);
device->setEventReceiver(&receiver);

   // 创建天空盒
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

smgr->addSkyBoxSceneNode(
   driver->getTexture("../../media/irrlicht2_up.jpg"),
   driver->getTexture("../../media/irrlicht2_dn.jpg"),
   driver->getTexture("../../media/irrlicht2_lf.jpg"),
   driver->getTexture("../../media/irrlicht2_rt.jpg"),
   driver->getTexture("../../media/irrlicht2_ft.jpg"),
   driver->getTexture("../../media/irrlicht2_bk.jpg"));

driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);


/*
OK了,接下来绘制一切,你应该明白了如何在Irr引擎中绘制地形了吧。
*/

int lastFPS = -1;

while(device->run())
if (device->isWindowActive())
{
   driver->beginScene(true, true, 0 );

   smgr->drawAll();
   env->drawAll();

   driver->endScene();

   // 在窗口标题中显示当前桢数
   int fps = driver->getFPS();
   if (lastFPS != fps)
   {
    core::stringw str = L"Terrain Renderer - Irrlicht Engine [";
    str += driver->getName();
    str += "] FPS:";
    str += fps;
    // 同时,显示当前摄象机所在位置的地形高度
    str += " Height: ";
    str += terrain->getHeight(camera->getAbsolutePosition().X, camera->getAbsolutePosition().Z);

    device->setWindowCaption(str.c_str());
    lastFPS = fps;
   }
}

device->drop();

return 0;
}