Irrlicht 3D Engine
 
Loading...
Searching...
No Matches
Tutorial 12: Terrain Rendering

This tutorial will briefly show how to use the terrain renderer of Irrlicht. It will also show the terrain renderer triangle selector to be able to do collision detection with terrain.

Note that the Terrain Renderer in Irrlicht is based on Spintz' GeoMipMapSceneNode, lots of thanks go to him. DeusXL provided a new elegant simple solution for building larger area on small heightmaps -> terrain smoothing.

In the beginning there is nothing special. We include the needed header files and create an event listener to listen if the user presses a key: The 'W' key switches to wireframe mode, the 'P' key to pointcloud mode, and the 'D' key toggles between solid and detail mapped material.

#include <irrlicht.h>
#include "driverChoice.h"
using namespace irr;
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
class MyEventReceiver : public IEventReceiver
{
public:
MyEventReceiver(scene::ISceneNode* terrain, scene::ISceneNode* skybox, scene::ISceneNode* skydome) :
Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true), showDebug(false)
{
Skybox->setVisible(showBox);
Skydome->setVisible(!showBox);
}
bool OnEvent(const SEvent& event)
{
// check if user presses the key 'W' or 'D'
{
switch (event.KeyInput.Key)
{
case irr::KEY_KEY_W: // switch wire frame mode
Terrain->setMaterialFlag(video::EMF_WIREFRAME,
!Terrain->getMaterial(0).Wireframe);
Terrain->setMaterialFlag(video::EMF_POINTCLOUD, false);
return true;
case irr::KEY_KEY_P: // switch wire frame mode
Terrain->setMaterialFlag(video::EMF_POINTCLOUD,
!Terrain->getMaterial(0).PointCloud);
Terrain->setMaterialFlag(video::EMF_WIREFRAME, false);
return true;
case irr::KEY_KEY_D: // toggle detail map
Terrain->setMaterialType(
Terrain->getMaterial(0).MaterialType == video::EMT_SOLID ?
video::EMT_DETAIL_MAP : video::EMT_SOLID);
return true;
case irr::KEY_KEY_S: // toggle skies
showBox=!showBox;
Skybox->setVisible(showBox);
Skydome->setVisible(!showBox);
return true;
case irr::KEY_KEY_X: // toggle debug information
showDebug=!showDebug;
Terrain->setDebugDataVisible(showDebug?scene::EDS_BBOX_ALL:scene::EDS_OFF);
return true;
default:
break;
}
}
return false;
}
private:
bool showBox;
bool showDebug;
};
Interface of an object which can receive events.
Scene node interface.
Definition ISceneNode.h:41
Main header file of the irrlicht, the only file needed to include.
Everything in the Irrlicht Engine can be found in this namespace.
Definition aabbox3d.h:13
@ KEY_KEY_P
Definition Keycodes.h:81
@ KEY_KEY_X
Definition Keycodes.h:89
@ KEY_KEY_D
Definition Keycodes.h:69
@ KEY_KEY_S
Definition Keycodes.h:84
@ KEY_KEY_W
Definition Keycodes.h:88
@ EET_KEY_INPUT_EVENT
A key input event.
EKEY_CODE Key
Key which has been pressed or released.
bool PressedDown
If not true, then the key was left up.
SEvents hold information about an event. See irr::IEventReceiver for details on event handling.
EEVENT_TYPE EventType
struct SKeyInput KeyInput

The start of the main function starts like in most other example. We ask the user for the desired renderer and start it up. This time with the advanced parameter handling.

int main()
{
// ask user for driver
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
if (driverType==video::EDT_COUNT)
return 1;
// create device with full flexibility over creation parameters
// you can add more parameters if desired, check irr::SIrrlichtCreationParameters
params.DriverType=driverType;
IrrlichtDevice* device = createDeviceEx(params);
if (device == 0)
return 1; // could not create selected driver.
The Irrlicht device. You can create it with createDevice() or createDeviceEx().
Axis aligned bounding box in 3d dimensional space.
Definition aabbox3d.h:22
E_DRIVER_TYPE
An enum for all types of drivers the Irrlicht Engine supports.
Structure for holding Irrlicht Device creation parameters.
core::dimension2d< u32 > WindowSize
Size of the window or the video mode in fullscreen mode. Default: 800x600.
video::E_DRIVER_TYPE DriverType
Type of video driver used to render graphics.

First, we add standard stuff to the scene: A nice irrlicht engine logo, a small help text, a user controlled camera, and we disable the mouse cursor.

video::IVideoDriver* driver = device->getVideoDriver();
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// add irrlicht logo
env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
//set other font
env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));
// add some help text
L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map\nPress 'S' to toggle skybox/skydome",
core::rect<s32>(10,421,250,475), true, true, 0, -1, true);
// add camera
smgr->addCameraSceneNodeFPS(0,100.0f,1.2f);
camera->setPosition(core::vector3df(2700*2,255*2,2600*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(42000.0f);
// disable mouse cursor
device->getCursorControl()->setVisible(false);
virtual gui::ICursorControl * getCursorControl()=0
Provides access to the cursor control.
virtual scene::ISceneManager * getSceneManager()=0
Provides access to the scene manager.
virtual video::IVideoDriver * getVideoDriver()=0
Provides access to the video driver for drawing 3d and 2d geometry.
virtual gui::IGUIEnvironment * getGUIEnvironment()=0
Provides access to the 2d user interface environment.
virtual void setVisible(bool visible)=0
Changes the visible state of the mouse cursor.
GUI Environment. Used as factory and manager of all other GUI elements.
virtual IGUIImage * addImage(video::ITexture *image, core::position2d< s32 > pos, bool useAlphaChannel=true, IGUIElement *parent=0, s32 id=-1, const wchar_t *text=0)=0
Adds an image element.
virtual IGUIFont * getFont(const io::path &filename)=0
Returns pointer to the font with the specified filename.
virtual IGUIStaticText * addStaticText(const wchar_t *text, const core::rect< s32 > &rectangle, bool border=false, bool wordWrap=true, IGUIElement *parent=0, s32 id=-1, bool fillBackground=false)=0
Adds a static text.
virtual IGUISkin * getSkin() const =0
Returns pointer to the current gui skin.
virtual void setFont(IGUIFont *font, EGUI_DEFAULT_FONT which=EGDF_DEFAULT)=0
sets a default font
Scene Node which is a (controlable) camera.
virtual void setTarget(const core::vector3df &pos)=0
Sets the look at target of the camera.
virtual void setFarValue(f32 zf)=0
Sets the value of the far clipping plane (default: 2000.0f)
The Scene Manager manages scene nodes, mesh recources, cameras and all the other stuff.
virtual ICameraSceneNode * addCameraSceneNodeFPS(ISceneNode *parent=0, f32 rotateSpeed=100.0f, f32 moveSpeed=0.5f, s32 id=-1, SKeyMap *keyMapArray=0, s32 keyMapSize=0, bool noVerticalMovement=false, f32 jumpSpeed=0.f, bool invertMouse=false, bool makeActive=true)=0
Adds a camera scene node with an animator which provides mouse and keyboard control appropriate for f...
virtual void setPosition(const core::vector3df &newpos)
Sets the position of the node relative to its parent.
Definition ISceneNode.h:507
Interface to driver which is able to perform 2d and 3d graphics functions.
virtual ITexture * getTexture(const io::path &filename)=0
Get access to a named texture.
virtual void setTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag, bool enabled=true)=0
Enables or disables a texture creation flag.

Here comes the terrain renderer scene node: We add it just like any other scene node to the scene using ISceneManager::addTerrainSceneNode(). The only parameter we use is a file name to the heightmap we use. A heightmap is simply a gray scale texture. The terrain renderer loads it and creates the 3D terrain from it.

To make the terrain look more big, we change the scale factor of it to (40, 4.4, 40). Because we don't have any dynamic lights in the scene, we switch off the lighting, and we set the file terrain-texture.jpg as texture for the terrain and detailmap3.jpg as second texture, called detail map. At last, we set the scale values for the texture: The first texture will be repeated only one time over the whole terrain, and the second one (detail map) 20 times.

// add terrain scene node
"../../media/terrain-heightmap.bmp",
0, // parent node
-1, // node id
core::vector3df(0.f, 0.f, 0.f), // position
core::vector3df(0.f, 0.f, 0.f), // rotation
core::vector3df(40.f, 4.4f, 40.f), // scale
video::SColor ( 255, 255, 255, 255 ), // vertexColor
5, // maxLOD
scene::ETPS_17, // patchSize
4 // smoothFactor
);
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);
virtual ITerrainSceneNode * addTerrainSceneNode(const io::path &heightMapFileName, ISceneNode *parent=0, s32 id=-1, const core::vector3df &position=core::vector3df(0.0f, 0.0f, 0.0f), const core::vector3df &rotation=core::vector3df(0.0f, 0.0f, 0.0f), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f), video::SColor vertexColor=video::SColor(255, 255, 255, 255), s32 maxLOD=5, E_TERRAIN_PATCH_SIZE patchSize=ETPS_17, s32 smoothFactor=0, bool addAlsoIfHeightmapEmpty=false)=0
Adds a terrain scene node to the scene graph.
void setMaterialTexture(u32 textureLayer, video::ITexture *texture)
Sets the texture of the specified layer in all materials of this scene node to the new texture.
Definition ISceneNode.h:436
void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
Sets all material flags at once to a new value.
Definition ISceneNode.h:425
void setMaterialType(video::E_MATERIAL_TYPE newType)
Sets the material type of all materials in this scene node to a new material type.
Definition ISceneNode.h:448
A scene node for displaying terrain using the geo mip map algorithm.
virtual void scaleTexture(f32 scale=1.0f, f32 scale2=0.0f)=0
Scales the base texture, similar to makePlanarTextureMapping.
Class representing a 32 bit ARGB color.
Definition SColor.h:202

To be able to do collision with the terrain, we create a triangle selector. If you want to know what triangle selectors do, just take a look into the collision tutorial. The terrain triangle selector works together with the terrain. To demonstrate this, we create a collision response animator and attach it to the camera, so that the camera will not be able to fly through the terrain.

// create triangle selector for the terrain
= smgr->createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
// create collision response animator and attach it to the camera
selector, camera, core::vector3df(60,100,60),
core::vector3df(0,50,0));
selector->drop();
camera->addAnimator(anim);
anim->drop();
bool drop() const
Drops the object. Decrements the reference counter by one.
virtual ITriangleSelector * createTerrainTriangleSelector(ITerrainSceneNode *node, s32 LOD=0)=0
Creates a triangle selector which can select triangles from a terrain scene node.
virtual ISceneNodeAnimatorCollisionResponse * createCollisionResponseAnimator(ITriangleSelector *world, ISceneNode *sceneNode, const core::vector3df &ellipsoidRadius=core::vector3df(30, 60, 30), const core::vector3df &gravityPerSecond=core::vector3df(0,-10.0f, 0), const core::vector3df &ellipsoidTranslation=core::vector3df(0, 0, 0), f32 slidingValue=0.0005f)=0
Creates a special scene node animator for doing automatic collision detection and response.
Animates a scene node. Can animate position, rotation, material, and so on.
virtual void addAnimator(ISceneNodeAnimator *animator)
Adds an animator which should animate this node.
Definition ISceneNode.h:348
virtual void setTriangleSelector(ITriangleSelector *selector)
Sets the triangle selector of the scene node.
Definition ISceneNode.h:635
Interface to return triangles with specific properties.

If you need access to the terrain data you can also do this directly via the following code fragment.

scene::CDynamicMeshBuffer* buffer = new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
terrain->getMeshBufferForLOD(*buffer, 0);
// Work on data or get the IndexBuffer with a similar call.
buffer->drop(); // When done drop the buffer again.
virtual IVertexBuffer & getVertexBuffer() const
virtual void getMeshBufferForLOD(IDynamicMeshBuffer &mb, s32 LOD=0) const =0
Gets the meshbuffer data based on a specified level of detail.
virtual void * getData()=0
Vertex with two texture coordinates.
Definition S3DVertex.h:110

To make the user be able to switch between normal and wireframe mode, we create an instance of the event receiver from above and let Irrlicht know about it. In addition, we add the skybox which we already used in lots of Irrlicht examples and a skydome, which is shown mutually exclusive with the skybox by pressing 'S'.

// create skybox and skydome
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
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"));
scene::ISceneNode* skydome=smgr->addSkyDomeSceneNode(driver->getTexture("../../media/skydome.jpg"),16,8,0.95f,2.0f);
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
// create event receiver
MyEventReceiver receiver(terrain, skybox, skydome);
device->setEventReceiver(&receiver);
virtual void setEventReceiver(IEventReceiver *receiver)=0
Sets a new user event receiver which will receive events from the engine.
virtual ISceneNode * addSkyBoxSceneNode(video::ITexture *top, video::ITexture *bottom, video::ITexture *left, video::ITexture *right, video::ITexture *front, video::ITexture *back, ISceneNode *parent=0, s32 id=-1)=0
Adds a skybox scene node to the scene graph.
virtual ISceneNode * addSkyDomeSceneNode(video::ITexture *texture, u32 horiRes=16, u32 vertRes=8, f32 texturePercentage=0.9, f32 spherePercentage=2.0, f32 radius=1000.f, ISceneNode *parent=0, s32 id=-1)=0
Adds a skydome scene node to the scene graph.

That's it, draw everything.

int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0 );
smgr->drawAll();
env->drawAll();
driver->endScene();
// display frames per second in window title
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Terrain Renderer - Irrlicht Engine [";
str += driver->getName();
str += "] FPS:";
str += fps;
// Also print terrain height of current camera position
// We can use camera position because terrain is located at coordinate origin
str += " Height: ";
str += terrain->getHeight(camera->getAbsolutePosition().X,
camera->getAbsolutePosition().Z);
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}
virtual bool run()=0
Runs the device.
virtual void setWindowCaption(const wchar_t *text)=0
Sets the caption of the window.
virtual bool isWindowActive() const =0
Returns if the window is active.
T X
X coordinate of the vector.
Definition vector3d.h:408
T Z
Z coordinate of the vector.
Definition vector3d.h:414
virtual void drawAll()=0
Draws all gui elements by traversing the GUI environment starting at the root node.
virtual void drawAll()=0
Draws all the scene nodes.
virtual core::vector3df getAbsolutePosition() const
Gets the absolute position of the node in world coordinates.
Definition ISceneNode.h:522
virtual f32 getHeight(f32 x, f32 y) const =0
Get height of a point of the terrain.
virtual bool beginScene(bool backBuffer=true, bool zBuffer=true, SColor color=SColor(255, 0, 0, 0), const SExposedVideoData &videoData=SExposedVideoData(), core::rect< s32 > *sourceRect=0)=0
Applications must call this method before performing any rendering.
virtual s32 getFPS() const =0
Returns current frames per second value.
virtual const wchar_t * getName() const =0
Gets name of this video driver.
virtual bool endScene()=0
Presents the rendered image to the screen.

Now you know how to use terrain in Irrlicht.