How to use the terrain engine libmini with Irrlicht

A forum to store posts deemed exceptionally wise and useful
knightoflight
Posts: 199
Joined: Sun Aug 24, 2003 5:47 pm
Location: Germany

How to use the terrain engine libmini with Irrlicht

Post by knightoflight »

Hello Irrlicht-community. I searched for a good terrain-engine on http://vterrain.org,
i found Stefan Roettgers terrainengine libmini. vterrain.org says:
"libMini by Stefan Röttger - implements his paper Real-Time Generation of Continuous Levels of Detail for Height Fields
LGPL source, highly optimized, 5 bytes/heixel, very fast"
I translate it with: " Quick and Cool !".

The terrain rendering algorithm (abbreviated GOLD) has also been included into the game AquaNox.

Normally it draws with OPENGL, but it also has a stubengine inside, where the user
has to write some interface-functions and the user can draw with other things.
I wrote the interface for Irrlicht. The first little example with the interface is below.
If you want to use it, first you have to download Stefan Roettgers engine:
http://www9.informatik.uni-erlangen.de/ ... /download/
Then you have to compile the stubengine - in UNIx/LINUX use the build.sh like written in the
README, with Microsoft c++/Relo you have to put in the parameter -DNOOGL
Then you can try to compile this first example. I will try to do an better example, maybe
i would need help, please mailto zenprogramming at yahoo dot de.
And many thanks for the help of Stefan Roettger and im sure i need more.

Code: Select all

// Example v0.2 for Roettgers libmini-terrainengine with Irrlicht
//
// The mini library is the core of the terrain renderer described in the
// paper "Real-Time Generation of Continuous Levels of Detail for Height Fields".
// Copyright (c) 1995-2004 by Stefan Roettger (Version 5.02 as of 18.August.2004).
//
// The terrain renderer is licensed under the terms of the LGPL (see
// http://www.gnu.org/copyleft/ for more information on the license).
// Any commercial use of the code or parts of it requires the explicit
// permission of the author!
//
// The author's contact address is:
//
// mailto:roettger@cs.fau.de
// http://www9.cs.fau.de/Persons/Roettger
//
// The original terrain rendering paper and the talk are available here:
//
// http://www9.cs.fau.de/Persons/Roettger/papers/TERRAIN.PDF
// http://www9.cs.fau.de/Persons/Roettger/papers/WSCG98.PPT
//
//
// example and Irrlicht-interface-functions by zenprogramming
// (with help from Stefan Roettger ;-) )
// mailto: zenprogramming at yahoo dot de
// http://zenprogramming.tripod.com

#include <irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#pragma comment(lib, "Irrlicht.lib")
#include "ministub.hpp"
scene::IAnimatedMeshSceneNode* terrainnode = 0;
scene::IAnimatedMesh* mesh = 0;
video::IVideoDriver* driver=0;
video::SMaterial material;
int triangleindex=-1;
int vertex0=0;
SMeshBuffer* buffer = 0;
video::S3DVertex vertex;

int size=3;
float dim=10.0f;
float scale=5.0f;

//libmini->Irrlichtinterfacefunctions
//this function starts every trianglefan
void mybeginfan()
{
   triangleindex=0;
   vertex0=buffer->getVertexCount();
}

//libmini->Irrlichtinterfacefunctions
//this function gives one vertex
void myfanvertex(float i,float y,float j)
{
   vertex.Pos.set(dim*(i-size/2),
      y*scale,
      dim*(size/2-j));
   // instead of using vertex-colors with a white texture,
   // you can use a normal texture
   // and you can use the vertex-colors for shadows
   vertex.Color.set(255,255/i,255/y,255/j);
   buffer->Vertices.push_back(vertex);
   if (triangleindex==2)
   {
      buffer->Indices.push_back(vertex0);
      buffer->Indices.push_back(buffer->getVertexCount()-2);
      buffer->Indices.push_back(buffer->getVertexCount()-1);
      return;
   }
   triangleindex++;
}
 

IAnimatedMesh* createTerrainMesh(SMeshBuffer* buffer)
{
   SMesh* mesh = new SMesh();
   SAnimatedMesh* animatedMesh = new SAnimatedMesh();
   for (s32 i=0; i<(s32)buffer->Indices.size(); i+=3)
   {
      core::plane3d<f32> p(
      buffer->Vertices[buffer->Indices[i+0]].Pos,
      buffer->Vertices[buffer->Indices[i+1]].Pos,
      buffer->Vertices[buffer->Indices[i+2]].Pos);
      p.Normal.normalize();

      buffer->Vertices[buffer->Indices[i+0]].Normal = p.Normal;
      buffer->Vertices[buffer->Indices[i+1]].Normal = p.Normal;
      buffer->Vertices[buffer->Indices[i+2]].Normal = p.Normal;
   }
   mesh->addMeshBuffer(buffer);
   animatedMesh->addMesh(mesh);
   mesh->drop();
   return animatedMesh;
}
 

int main(int argc,char *argv[])
{ 
	IrrlichtDevice *device =
	createDevice(video::EDT_DIRECTX8, core::dimension2d<s32>(640, 480));
	driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();
	material.Texture1=0;
	material.Lighting=false;
	driver->setMaterial(material);
	short int hfield[]={0,0,0,
			            0,1,0,
				        0,0,0};
	ministub *stub;
	stub=new ministub(hfield,
		&size,&dim,scale,1.0f,
        mybeginfan,myfanvertex,0,0, NULL);
	float res=1.0f;
	float ex=0.0f,ey=10.0f,ez=30.0f;
	float dx=0.0f,dy=-0.25f,dz=-1.0f;
	float ux=0.0f,uy=1.0f,uz=0.0f;
	float fovy=60.0f;
	float aspect=1.0f;
	float nearp=1.0f;
	float farp=100.0f;

	int lastFPS = -1;
	vertex.Normal.set(0,1,0);
	vertex.TCoords.set(0,1);
	vertex.Color.set(255,255,255,255);



	// this part from the new buffer to the buffer drop
	// can generate a terrain mesh like in this case one
	// time static, or in the main-loop every frame
	// if you put it there
	buffer = new SMeshBuffer();
	stub->draw(res,
		ex,ey,ez,
        dx,dy,dz,
        ux,uy,uz,
        fovy,aspect,
        nearp,farp);
	mesh = createTerrainMesh(buffer);
	smgr->getMeshManipulator()->makePlanarTextureMapping(
	mesh->getMesh(0), 0.075f);
	buffer->drop();
	
	
	
	terrainnode = smgr->addAnimatedMeshSceneNode(mesh);
	terrainnode->setPosition(core::vector3df(-50,45,-60));
	terrainnode->setMaterialFlag(video::EMF_LIGHTING, false);
	terrainnode->setMaterialTexture(0, driver->getTexture("texture.BMP"));

	scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
	camera->setPosition(core::vector3df(-50,50,-100));

	while(device->run())
	{
		driver->beginScene(true, true, video::SColor(0,100,100,100));
   
		smgr->drawAll();
		driver->endScene();
		int fps = driver->getFPS();
		if (lastFPS != fps)
		{
			wchar_t tmp[1024];
			swprintf(tmp, 1024, L"Roettgers Terrainengine-libmini and Irrlicht Engine by zenprogramming (fps:%d) Triangles:%d", 
				fps, driver->getPrimitiveCountDrawn());
			device->setWindowCaption(tmp);
			lastFPS = fps;
		}
	}
	delete(stub);
	buffer->drop();
	device->drop();
 
	return 0;
}
Last edited by knightoflight on Sat Oct 16, 2004 8:01 am, edited 2 times in total.
bal
Posts: 829
Joined: Fri Jun 18, 2004 5:19 pm
Location: Geluwe, Belgium

Post by bal »

Could you post a binary show-off exaple :) ?
General Tools List
General FAQ
System: AMD Barton 2600+, 512MB, 9600XT 256MB, WinXP + FC3
puh
Posts: 356
Joined: Tue Aug 26, 2003 3:53 pm

Post by puh »

Yeah, will be very interesting!
brc-not-logged

Post by brc-not-logged »

I am VERY interested in this! Nice work! Please show us a binary! Also what type of LOD does libmini use?

Thanks and very nice work,
Mike
knightoflight
Posts: 199
Joined: Sun Aug 24, 2003 5:47 pm
Location: Germany

Post by knightoflight »

Hi,
@brc: Please read Stefan Roettgers papers for the type of LOD (pdf and ppt-download links above)

@bal, puh, brc for the binary:
I put a binaryzip in my downloadarea (for windows use w,a,s,d to scroll)
http://zenprogramming.tripod.com
You could create a demo or project easy if you expand the first example. But i made a better second example with the terrainengine in the mainloop creating a new terrainmesh every frame so you can see it in real action.

Code: Select all

// Example Mars for libmini-terrainengine with Irrlicht
// v.01 16.10.2004
// v.02 17.10.2004, instead of makeplanartexturemapping TCoords.set
// use W,A,S,D to scroll and see how the mesh is changed inside the
// eyefield
// For the demo the FPS-camera is active and you can look around
// naturally in a game you would exchange it with a static camera
//
// The mini library is the core of the terrain renderer described in the
// paper "Real-Time Generation of Continuous Levels of Detail for Height Fields".
// Copyright (c) 1995-2004 by Stefan Roettger (Version 5.02 as of 18.August.2004).
//
// The terrain renderer is licensed under the terms of the LGPL (see
// http://www.gnu.org/copyleft/ for more information on the license).
// Any commercial use of the code or parts of it requires the explicit
// permission of the author!
//
// The libmini-author's contact address is:
//
// mailto:roettger@cs.fau.de
// http://www9.cs.fau.de/Persons/Roettger
//
// The original terrain rendering paper and the talk are available here:
//
// http://www9.cs.fau.de/Persons/Roettger/papers/TERRAIN.PDF
// http://www9.cs.fau.de/Persons/Roettger/papers/WSCG98.PPT
//
//
// example and Irrlicht-interface-functions by zenprogramming
// (with help from Stefan Roettger ;-) )
// mailto: zenprogramming at yahoo dot de
// http://zenprogramming.tripod.com

#include <irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#pragma comment(lib, "Irrlicht.lib")
#include "ministub.hpp"


scene::IAnimatedMesh* mesh = 0;
video::IVideoDriver* driver=0;
scene::ICameraSceneNode* camera=0;
video::SMaterial material;
int triangleindex=-1;
int vertex0=0;
SMeshBuffer* buffer = 0;
video::S3DVertex vertex;
core::dimension2d<f32> tx;
vector3df cameraPos;
int cameraoffset=120;

int size=256;
float dim=1.0f;
float scale=0.2f;
float res=1000.0f;
float ex=0.0f,ey=10.0f,ez=-30.0f;
float dx=0.0f,dy=-0.25f,dz=-1.0f;
float ux=0.0f,uy=1.0f,uz=0.0f;
float fovy=100.0f;
float aspect=1.0f;
float nearp=1.0f;
float farp=1000.0f;



//libmini->Irrlichtinterfacefunctions
//this function starts every trianglefan
void mybeginfan()
{
	triangleindex=0;
	vertex0=buffer->getVertexCount();
}

//libmini->Irrlichtinterfacefunctions
//this function gives one vertex
void myfanvertex(float i,float y,float j)
{
	vertex.Pos.set(dim*i-size/2*dim,
		y*scale,
		size/2*dim-dim*j);
	vertex.TCoords.set(1.0f-(float)i/(size-1),
                      1.0f-(float)j/(size-1));	// instead of using white vertex-colors with a texture,
	// you could use the vertex-colors for shadows from heightdelta
	// vertex.Color.set(255,from heightdelta,from heightdelta,from heightdelta);
	buffer->Vertices.push_back(vertex);
	if (triangleindex==2)
	{
		buffer->Indices.push_back(vertex0);
		buffer->Indices.push_back(buffer->getVertexCount()-2);
		buffer->Indices.push_back(buffer->getVertexCount()-1);
		return;
	}
	triangleindex++;
}
 

class MyEventReceiver : public IEventReceiver
{
public:
	virtual bool OnEvent(SEvent event)
	{

		if (event.EventType == irr::EET_KEY_INPUT_EVENT )
		{
			switch(event.KeyInput.Key)
			{
				case KEY_KEY_W:
				{
					ez=ez+2.0;
					cameraPos=camera->getPosition();
					cameraPos.Z = ez-cameraoffset;
					camera->setPosition(cameraPos);
					break;
				}
				case KEY_KEY_S:
				{
					ez=ez-2.0;
					cameraPos=camera->getPosition();
					cameraPos.Z = ez-cameraoffset;
					camera->setPosition(cameraPos);
					break;
				}
				case KEY_KEY_D:
				{
					ex=ex+5.0;
					cameraPos=camera->getPosition();
					cameraPos.X = ex;
					camera->setPosition(cameraPos);
					break;
				}
				case KEY_KEY_A:
				{
					ex=ex-5.0;
					cameraPos=camera->getPosition();
					cameraPos.X = ex;
					camera->setPosition(cameraPos);
					break;
				}
				return true;
			}
		}

		return false;
	}
};

int main(int argc,char *argv[])
{ 
	MyEventReceiver receiver;
	IrrlichtDevice *device =
	createDevice(video::EDT_DIRECTX8, core::dimension2d<s32>(640, 480),16, false, false, &receiver);
	//In Irrlichtversions 0.7/0.71 you should use this line instead of the line above
	//createDevice(video::EDT_DIRECTX8, core::dimension2d<s32>(640, 480),16, false, false, false, &receiver);

	driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();
	material.Texture1=0;
	material.Lighting=false;
	driver->setMaterial(material);

    IImage* heightimage = driver->createImageFromFile("FaceOfMarsMap.bmp");
	int width = heightimage->getDimension().Width; 
	size = width;
	int height = heightimage->getDimension().Height;
	short int *hfield=new short int[width*height];
	for (int i=0; i<width; i++)
	{
		for (int j=0;j<height;j++)
		{
			hfield[i+j*height]=heightimage->getPixel(i, j).getRed();
		}
	}

	tx.Width = 1.0f / ((size-1)*dim);
	tx.Height = 1.0f / ((size-1)*dim);

	ministub *stub;
	stub=new ministub(hfield,
		&size,&dim,scale,1.0f,
        mybeginfan,myfanvertex,0,0,
        NULL);

	int lastFPS = -1;
	vertex.Normal.set(0,1,0);
	vertex.TCoords.set(0,1);
	vertex.Color.set(255,255,255,255);

	scene::IAnimatedMeshSceneNode* terrainnode = 0;
	ITexture* terraintexture = driver->getTexture("faceofmars.jpg");

	camera = smgr->addCameraSceneNodeFPS(0, 100, 100);
	camera->setPosition(core::vector3df(0,50,-150));

	while(device->run())
	{

		buffer = new SMeshBuffer();

		stub->draw(res,
			ex,ey,ez,
			dx,dy,dz,
			ux,uy,uz,
			fovy,aspect,
			nearp,farp);

		SMesh* meshtmp = new SMesh();
		SAnimatedMesh* animatedMesh = new SAnimatedMesh();
		meshtmp->addMeshBuffer(buffer);
		animatedMesh->addMesh(meshtmp);
		mesh = animatedMesh;
		buffer->drop();
		terrainnode = smgr->addAnimatedMeshSceneNode(mesh);
		terrainnode->setMaterialFlag(video::EMF_LIGHTING, false);
		terrainnode->setMaterialTexture(0, terraintexture);

		driver->beginScene(true, true, video::SColor(0,150,50,0));
   
		smgr->drawAll();
		driver->endScene();
		int fps = driver->getFPS();
		if (lastFPS != fps)
		{
			wchar_t tmp[1024];
			swprintf(tmp, 1024, L"Marsdemo: Terrainengine-libmini and Irrlicht Engine (fps:%d) Triangles:%d", 
				fps, driver->getPrimitiveCountDrawn());
			device->setWindowCaption(tmp);
			lastFPS = fps;
		}
		terrainnode->remove();
		meshtmp->drop();
		animatedMesh->drop();
	}
	delete(stub);
	delete(hfield);
	heightimage->drop();
	device->drop();
 
	return 0;
}

Last edited by knightoflight on Wed Oct 20, 2004 2:02 pm, edited 3 times in total.
knightoflight
Posts: 199
Joined: Sun Aug 24, 2003 5:47 pm
Location: Germany

Post by knightoflight »

New Version faceofmars-Demo:
For all who downloaded the FaceOfMars-demo before Sunday 17.10.2004, 13:20 - i made a new version v0.2, now instead of makeplanartexturemapping with TCoords.set, the tex now is in a better position, and i changed the v0.1 to v0.2 version above.
brc-not-logged

Post by brc-not-logged »

Good job with fixing the texture stuff :).

The map is pretty small, consisting of only of about 250 triangles, is there anyway we could get some FPS measurments on a large one?

Also, on some parts with moving the camera around and such, there are noticable cracks in the terrain.

Furthermore, the camera acts WEIRD. Using WASD, it sort of forms the terrain (foward makes it appear out of nowhere, on all sides sort of) but then using directional arrows you can navigate the map as it is without it changing. Is there anyway the map could be shown in it's entirety on state-up and just navigate it with the directional keys?

Thanks and good work,
Mike
knightoflight
Posts: 199
Joined: Sun Aug 24, 2003 5:47 pm
Location: Germany

Post by knightoflight »

Hi brc,
first answer to this lines:
>Furthermore, the camera acts WEIRD. Using WASD, it sort of forms the >terrain (foward makes it appear out of nowhere, on all sides sort of) but >then using directional arrows you can navigate the map as it is without it
>changing. Is there anyway the map could be shown in it's entirety on state-
>up and just navigate it with the directional keys?
The terrain is build from the eyepoint ex,ey,ez (look to the code)
The camera starts at the eyepoint and follows it if you use wasd.
If you use the FPS-camera with the directional keys, you break the illusion and can fly over the terrain to see the demo. its only for the demo, but its not a bug and you would NEVER do it in a game, cause it breaks the illusion, if you dont want it change the FPS camera to a static camera, ok ?
knightoflight
Posts: 199
Joined: Sun Aug 24, 2003 5:47 pm
Location: Germany

Post by knightoflight »

For V0.3 - first change:
If you want to delete the vertex behind the line of sight, insert the line
if ((size/2*dim-dim*j)< ez-cameraoffset) return;
as first line in myfanvertex. Then the vertex behind you are not given to the vertexbuffer and to Irrlicht. cameraoffset in the faceofmarsdemo=120

Edit: 18.10.2004, 19:15 - ok edited the version above to v0.3, created a new version for download
brcolow
Posts: 56
Joined: Mon Jul 19, 2004 6:15 am
Location: Arizona

Post by brcolow »

Hmm I guess the problem is really just like it shows WAY too little of the map at a time. You can TOTALLY see the cut-off and when moving it looks unatural, how can this be fixed?
G'Day.
knightoflight
Posts: 199
Joined: Sun Aug 24, 2003 5:47 pm
Location: Germany

Post by knightoflight »

The last change with the line
if ((size/2*dim-dim*j)< ez-cameraoffset) return;
isnt perfect, it would be better to sort the buffer after creating, maybe i change

@brcolow: only a bug could be fixed, but i see no bug, please explain better what you mean ?
bal
Posts: 829
Joined: Fri Jun 18, 2004 5:19 pm
Location: Geluwe, Belgium

Post by bal »

I get this, nothing more, nothing less...

Image
General Tools List
General FAQ
System: AMD Barton 2600+, 512MB, 9600XT 256MB, WinXP + FC3
The Hellcat
Posts: 33
Joined: Sat Oct 16, 2004 11:52 pm
Location: Earth

Post by The Hellcat »

I get the same thing, bal...
HTML/CSS/Javascript/ASP/PHP/SQL/XML/QBasic
Next Up: C++
brc-not-logged

Post by brc-not-logged »

Bal, try moving around with WASD.
knightoflight
Posts: 199
Joined: Sun Aug 24, 2003 5:47 pm
Location: Germany

Post by knightoflight »

sorry for the misunderstanding that its a demo for scrolling with keys wasd, but i wrote in the readme and in the source - ok i never read such things, too and start trying ;-)
I will try to change ...
Post Reply