/** \page example001 Tutorial 1: HelloWorld
 \image html "001shot.jpg"
 \image latex "001shot.jpg"

This Tutorial shows how to set up the IDE for using the Irrlicht Engine and how
to write a simple HelloWorld program with it. The program will show how to use
the basics of the VideoDriver, the GUIEnvironment, and the SceneManager.
Microsoft Visual Studio is used as an IDE, but you will also be able to
understand everything if you are using a different one or even another
operating system than windows.

You have to include the header file <irrlicht.h> in order to use the engine. The
header file can be found in the Irrlicht Engine SDK directory \c include. To let
the compiler find this header file, the directory where it is located has to be
specified. This is different for every IDE and compiler you use. Let's explain
shortly how to do this in Microsoft Visual Studio:

- If you use Version 6.0, select the Menu Extras -> Options.
  Select the directories tab, and select the 'Include' Item in the combo box.
  Add the \c include directory of the irrlicht engine folder to the list of
  directories. Now the compiler will find the Irrlicht.h header file. We also
  need the irrlicht.lib to be found, so stay in that dialog, select 'Libraries'
  in the combo box and add the \c lib/VisualStudio directory.
  \image html "vc6optionsdir.jpg"
  \image latex "vc6optionsdir.jpg"
  \image html "vc6include.jpg"
  \image latex "vc6include.jpg"

- If your IDE is Visual Studio .NET, select Tools -> Options.
  Select the projects entry and then select VC++ directories. Select 'show
  directories for include files' in the combo box, and add the \c include
  directory of the irrlicht engine folder to the list of directories. Now the
  compiler will find the Irrlicht.h header file. We also need the irrlicht.lib
  to be found, so stay in that dialog, select 'show directories for Library
  files' and add the \c lib/VisualStudio directory.
  \image html "vcnetinclude.jpg"
  \image latex "vcnetinclude.jpg"

That's it. With your IDE set up like this, you will now be able to develop
applications with the Irrlicht Engine.

Lets start!

After we have set up the IDE, the compiler will know where to find the Irrlicht
Engine header files so we can include it now in our code.
\code
#include <irrlicht.h>

\endcode

In the Irrlicht Engine, everything can be found in the namespace 'irr'. So if
you want to use a class of the engine, you have to write irr:: before the name
of the class. For example to use the IrrlichtDevice write: irr::IrrlichtDevice.
To get rid of the irr:: in front of the name of every class, we tell the
compiler that we use that namespace from now on, and we will not have to write
irr:: anymore.
\code
using namespace irr;

\endcode

There are 5 sub namespaces in the Irrlicht Engine. Take a look at them, you can
read a detailed description of them in the documentation by clicking on the top
menu item 'Namespace List' or by using this link:
http://irrlicht.sourceforge.net/docu/namespaces.html
Like the irr namespace, we do not want these 5 sub namespaces now, to keep this
example simple. Hence, we tell the compiler again that we do not want always to
write their names.
\code
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

\endcode

To be able to use the Irrlicht.DLL file, we need to link with the Irrlicht.lib.
We could set this option in the project settings, but to make it easy, we use a
pragma comment lib for VisualStudio. On Windows platforms, we have to get rid
of the console window, which pops up when starting a program with main(). This
is done by the second pragma. We could also use the WinMain method, though
losing platform independence then.
\code
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif


\endcode

This is the main method. We can now use main() on every platform.
\code
int main()
{
	\endcode

	The most important function of the engine is the createDevice()
	function. The IrrlichtDevice is created by it, which is the root
	object for doing anything with the engine. createDevice() has 7
	parameters:

	- deviceType: Type of the device. This can currently be the Null-device,
	   one of the two software renderers, D3D8, D3D9, or OpenGL. In this
	   example we use EDT_SOFTWARE, but to try out, you might want to
	   change it to EDT_BURNINGSVIDEO, EDT_NULL, EDT_DIRECT3D8,
	   EDT_DIRECT3D9, or EDT_OPENGL.

	- windowSize: Size of the Window or screen in FullScreenMode to be
	   created. In this example we use 640x480.

	- bits: Amount of color bits per pixel. This should be 16 or 32. The
	   parameter is often ignored when running in windowed mode.

	- fullscreen: Specifies if we want the device to run in fullscreen mode
	   or not.

	- stencilbuffer: Specifies if we want to use the stencil buffer (for
	   drawing shadows).

	- vsync: Specifies if we want to have vsync enabled, this is only useful
	   in fullscreen mode.

	- eventReceiver: An object to receive events. We do not want to use this
	   parameter here, and set it to 0.

	Always check the return value to cope with unsupported drivers,
	dimensions, etc.
	\code
	IrrlichtDevice *device =
		createDevice( video::EDT_SOFTWARE, dimension2d<u32>(640, 480), 16,
			false, false, false, 0);

	if (!device)
		return 1;

	\endcode

	Set the caption of the window to some nice text. Note that there is an
	'L' in front of the string. The Irrlicht Engine uses wide character
	strings when displaying text.
	\code
	device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");

	\endcode

	Get a pointer to the VideoDriver, the SceneManager and the graphical
	user interface environment, so that we do not always have to write
	device->getVideoDriver(), device->getSceneManager(), or
	device->getGUIEnvironment().
	\code
	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();

	\endcode

	We add a hello world label to the window, using the GUI environment.
	The text is placed at the position (10,10) as top left corner and
	(260,22) as lower right corner.
	\code
	guienv->addStaticText(L"Hello World! This is the Irrlicht Software renderer!",
		rect<s32>(10,10,260,22), true);

	\endcode

	To show something interesting, we load a Quake 2 model and display it.
	We only have to get the Mesh from the Scene Manager with getMesh() and add
	a SceneNode to display the mesh with addAnimatedMeshSceneNode(). We
	check the return value of getMesh() to become aware of loading problems
	and other errors.

	Instead of writing the filename sydney.md2, it would also be possible
	to load a Maya object file (.obj), a complete Quake3 map (.bsp) or any
	other supported file format. By the way, that cool Quake 2 model
	called sydney was modelled by Brian Collins.
	\code
	IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
	if (!mesh)
	{
		device->drop();
		return 1;
	}
	IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );

	\endcode

	To let the mesh look a little bit nicer, we change its material. We
	disable lighting because we do not have a dynamic light in here, and
	the mesh would be totally black otherwise. Then we set the frame loop,
	such that the predefined STAND animation is used. And last, we apply a
	texture to the mesh. Without it the mesh would be drawn using only a
	color.
	\code
	if (node)
	{
		node->setMaterialFlag(EMF_LIGHTING, false);
		node->setMD2Animation(scene::EMAT_STAND);
		node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
	}

	\endcode

	To look at the mesh, we place a camera into 3d space at the position
	(0, 30, -40). The camera looks from there to (0,5,0), which is
	approximately the place where our md2 model is.
	\code
	smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

	\endcode

	Ok, now we have set up the scene, lets draw everything: We run the
	device in a while() loop, until the device does not want to run any
	more. This would be when the user closes the window or presses ALT+F4
	(or whatever keycode closes a window).
	\code
	while(device->run())
	{
		\endcode

		Anything can be drawn between a beginScene() and an endScene()
		call. The beginScene() call clears the screen with a color and
		the depth buffer, if desired. Then we let the Scene Manager and
		the GUI Environment draw their content. With the endScene()
		call everything is presented on the screen.
		\code
		driver->beginScene(true, true, SColor(255,100,101,140));

		smgr->drawAll();
		guienv->drawAll();

		driver->endScene();
	}

	\endcode

	After we are done with the render loop, we have to delete the Irrlicht
	Device created before with createDevice(). In the Irrlicht Engine, you
	have to delete all objects you created with a method or function which
	starts with 'create'. The object is simply deleted by calling ->drop().
	See the documentation at irr::IReferenceCounted::drop() for more
	information.
	\code
	device->drop();

	return 0;
}

\endcode

That's it. Compile and run.
**/
/** \page example002 Tutorial 2: Quake3Map
 \image html "002shot.jpg"
 \image latex "002shot.jpg"

This Tutorial shows how to load a Quake 3 map into the engine, create a
SceneNode for optimizing the speed of rendering, and how to create a user
controlled camera.

Please note that you should know the basics of the engine before starting this
tutorial. Just take a short look at the first tutorial, if you haven't done
this yet: http://irrlicht.sourceforge.net/tut001.html

Lets start like the HelloWorld example: We include the irrlicht header files
and an additional file to be able to ask the user for a driver type using the
console.
\code
#include <irrlicht.h>
#include <iostream>

\endcode

As already written in the HelloWorld example, in the Irrlicht Engine everything
can be found in the namespace 'irr'. To get rid of the irr:: in front of the
name of every class, we tell the compiler that we use that namespace from now
on, and we will not have to write that 'irr::'. There are 5 other sub
namespaces 'core', 'scene', 'video', 'io' and 'gui'. Unlike in the HelloWorld
example, we do not call 'using namespace' for these 5 other namespaces, because
in this way you will see what can be found in which namespace. But if you like,
you can also include the namespaces like in the previous example.
\code
using namespace irr;

\endcode

Again, to be able to use the Irrlicht.DLL file, we need to link with the
Irrlicht.lib. We could set this option in the project settings, but to make it
easy, we use a pragma comment lib:
\code
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

\endcode

Ok, lets start. Again, we use the main() method as start, not the WinMain().
\code
int main()
{
	\endcode

	Like in the HelloWorld example, we create an IrrlichtDevice with
	createDevice(). The difference now is that we ask the user to select
	which video driver to use. The Software device might be
	too slow to draw a huge Quake 3 map, but just for the fun of it, we make
	this decision possible, too.
	\code

	// ask user for driver

	video::E_DRIVER_TYPE driverType;

	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;
	}

	// create device and exit if creation failed

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

	if (device == 0)
		return 1; // could not create selected driver.

	\endcode

	Get a pointer to the video driver and the SceneManager so that
	we do not always have to call irr::IrrlichtDevice::getVideoDriver() and
	irr::IrrlichtDevice::getSceneManager().
	\code
	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();

	\endcode

	To display the Quake 3 map, we first need to load it. Quake 3 maps
	are packed into .pk3 files which are nothing else than .zip files.
	So we add the .pk3 file to our irr::io::IFileSystem. After it was added,
	we are able to read from the files in that archive as if they are
	directly stored on the disk.
	\code
	device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");

	\endcode

	Now we can load the mesh by calling
	irr::scene::ISceneManager::getMesh(). We get a pointer returned to an
	irr::scene::IAnimatedMesh. As you might know, Quake 3 maps are not
	really animated, they are only a huge chunk of static geometry with
	some materials attached. Hence the IAnimatedMesh consists of only one
	frame, so we get the "first frame" of the "animation", which is our
	quake level and create an Octree scene node with it, using
	irr::scene::ISceneManager::addOctreeSceneNode().
	The Octree optimizes the scene a little bit, trying to draw only geometry
	which is currently visible. An alternative to the Octree would be a
	irr::scene::IMeshSceneNode, which would always draw the complete
	geometry of the mesh, without optimization. Try it: Use
	irr::scene::ISceneManager::addMeshSceneNode() instead of
	addOctreeSceneNode() and compare the primitives drawn by the video
	driver. (There is a irr::video::IVideoDriver::getPrimitiveCountDrawn()
	method in the irr::video::IVideoDriver class). Note that this
	optimization with the Octree is only useful when drawing huge meshes
	consisting of lots of geometry.
	\code
	scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
	scene::ISceneNode* node = 0;

	if (mesh)
		node = smgr->addOctreeSceneNode(mesh->getMesh(0), 0, -1, 1024);
//		node = smgr->addMeshSceneNode(mesh->getMesh(0));

	\endcode

	Because the level was not modelled around the origin (0,0,0), we
	translate the whole level a little bit. This is done on
	irr::scene::ISceneNode level using the methods
	irr::scene::ISceneNode::setPosition() (in this case),
	irr::scene::ISceneNode::setRotation(), and
	irr::scene::ISceneNode::setScale().
	\code
	if (node)
		node->setPosition(core::vector3df(-1300,-144,-1249));

	\endcode

	Now we only need a camera to look at the Quake 3 map.
	We want to create a user controlled camera. There are some
	cameras available in the Irrlicht engine. For example the
	MayaCamera which can be controlled like the camera in Maya:
	Rotate with left mouse button pressed, Zoom with both buttons pressed,
	translate with right mouse button pressed. This could be created with
	irr::scene::ISceneManager::addCameraSceneNodeMaya(). But for this
	example, we want to create a camera which behaves like the ones in
	first person shooter games (FPS) and hence use
	irr::scene::ISceneManager::addCameraSceneNodeFPS().
	\code
	smgr->addCameraSceneNodeFPS();

	\endcode

	The mouse cursor needs not be visible, so we hide it via the
	irr::IrrlichtDevice::ICursorControl.
	\code
	device->getCursorControl()->setVisible(false);

	\endcode

	We have done everything, so lets draw it. We also write the current
	frames per second and the primitives drawn into the caption of the
	window. The test for irr::IrrlichtDevice::isWindowActive() is optional,
	but prevents the engine to grab the mouse cursor after task switching
	when other programs are active. The call to
	irr::IrrlichtDevice::yield() will avoid the busy loop to eat up all CPU
	cycles when the window is not active.
	\code
	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();
	}

	\endcode

	In the end, delete the Irrlicht device.
	\code
	device->drop();
	return 0;
}

\endcode

That's it. Compile and play around with the program.
**/
/** \page example003 Tutorial 3: Custom SceneNode
 \image html "003shot.jpg"
 \image latex "003shot.jpg"

This Tutorial is more advanced than the previous ones.
If you are currently just playing around with the Irrlicht
engine, you may want to look at other examples first.
This tutorials shows how to create a custom scene node and
how to use it in the engine. A custom scene node is needed
if you want to implement a render technique the Irrlicht
Engine currently does not support. For example, you can write
an indoor portal based renderer or an advanced terrain scene
node with it. By creating custom scene nodes, you can
easily extend the Irrlicht Engine and adapt it to your own
needs.

I will keep the tutorial simple: Keep everything very
short, everything in one .cpp file, and I'll use the engine
here as in all other tutorials.

To start, I include the header files, use the irr namespace,
and tell the linker to link with the .lib file.
\code
#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

\endcode

Here comes the more sophisticated part of this tutorial:
The class of our very own custom scene node. To keep it simple,
our scene node will not be an indoor portal renderer nor a terrain
scene node, but a simple tetraeder, a 3d object consisting of 4
connected vertices, which only draws itself and does nothing more.
Note that this scenario does not require a custom scene node in Irrlicht.
Instead one would create a mesh from the geometry and pass it to a
irr::scene::IMeshSceneNode. This example just illustrates creation of a custom
scene node in a very simple setting.

To let our scene node be able to be inserted into the Irrlicht
Engine scene, the class we create needs to be derived from the
irr::scene::ISceneNode class and has to override some methods.
\code

class CSampleSceneNode : public scene::ISceneNode
{

	\endcode

	First, we declare some member variables:
	The bounding box, 4 vertices, and the material of the tetraeder.
	\code
	core::aabbox3d<f32> Box;
	video::S3DVertex Vertices[4];
	video::SMaterial Material;

	\endcode

	The parameters of the constructor specify the parent of the scene node,
	a pointer to the scene manager, and an id of the scene node.
	In the constructor we call the parent class' constructor,
	set some properties of the material, and
	create the 4 vertices of the tetraeder we will draw later.
	\code

public:

	CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
		: scene::ISceneNode(parent, mgr, id)
	{
		Material.Wireframe = false;
		Material.Lighting = false;

		Vertices[0] = video::S3DVertex(0,0,10, 1,1,0,
				video::SColor(255,0,255,255), 0, 1);
		Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0,
				video::SColor(255,255,0,255), 1, 1);
		Vertices[2] = video::S3DVertex(0,20,0, 0,1,1,
				video::SColor(255,255,255,0), 1, 0);
		Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1,
				video::SColor(255,0,255,0), 0, 0);

	\endcode

	The Irrlicht Engine needs to know the bounding box of a scene node.
	It will use it for automatic culling and other things. Hence, we
	need to create a bounding box from the 4 vertices we use.
	If you do not want the engine to use the box for automatic culling,
	and/or don't want to create the box, you could also call
	irr::scene::ISceneNode::setAutomaticCulling() with irr::scene::EAC_OFF.
	\code
		Box.reset(Vertices[0].Pos);
		for (s32 i=1; i<4; ++i)
			Box.addInternalPoint(Vertices[i].Pos);
	}

	\endcode

	Before it is drawn, the irr::scene::ISceneNode::OnRegisterSceneNode()
	method of every scene node in the scene is called by the scene manager.
	If the scene node wishes to draw itself, it may register itself in the
	scene manager to be drawn. This is necessary to tell the scene manager
	when it should call irr::scene::ISceneNode::render(). For
	example, normal scene nodes render their content one after another,
	while stencil buffer shadows would like to be drawn after all other
	scene nodes. And camera or light scene nodes need to be rendered before
	all other scene nodes (if at all). So here we simply register the
	scene node to render normally. If we would like to let it be rendered
	like cameras or light, we would have to call
	SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA);
	After this, we call the actual
	irr::scene::ISceneNode::OnRegisterSceneNode() method of the base class,
	which simply lets also all the child scene nodes of this node register
	themselves.
	\code
	virtual void OnRegisterSceneNode()
	{
		if (IsVisible)
			SceneManager->registerNodeForRendering(this);

		ISceneNode::OnRegisterSceneNode();
	}

	\endcode

	In the render() method most of the interesting stuff happens: The
	Scene node renders itself. We override this method and draw the
	tetraeder.
	\code
	virtual void render()
	{
		u16 indices[] = {	0,2,3, 2,1,3, 1,0,3, 2,0,1	};
		video::IVideoDriver* driver = SceneManager->getVideoDriver();

		driver->setMaterial(Material);
		driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
		driver->drawVertexPrimitiveList(&Vertices[0], 4, &indices[0], 4, video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
	}

	\endcode

	And finally we create three small additional methods.
	irr::scene::ISceneNode::getBoundingBox() returns the bounding box of
	this scene node, irr::scene::ISceneNode::getMaterialCount() returns the
	amount of materials in this scene node (our tetraeder only has one
	material), and irr::scene::ISceneNode::getMaterial() returns the
	material at an index. Because we have only one material here, we can
	return the only one material, assuming that no one ever calls
	getMaterial() with an index greater than 0.
	\code
	virtual const core::aabbox3d<f32>& getBoundingBox() const
	{
		return Box;
	}

	virtual u32 getMaterialCount() const
	{
		return 1;
	}

	virtual video::SMaterial& getMaterial(u32 i)
	{
		return Material;
	}	
};

\endcode

That's it. The Scene node is done. Now we simply have to start
the engine, create the scene node and a camera, and look at the result.
\code
int main()
{
	// let user select driver type

	video::E_DRIVER_TYPE driverType;

	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 0;
	}

	// create device

	IrrlichtDevice *device = createDevice(driverType,
			core::dimension2d<u32>(640, 480), 16, false);
		
	if (device == 0)
		return 1; // could not create selected driver.

	// create engine and camera

	device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo");

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

	smgr->addCameraSceneNode(0, core::vector3df(0,-40,0), core::vector3df(0,0,0));

	\endcode

	Create our scene node. I don't check the result of calling new, as it
	should throw an exception rather than returning 0 on failure. Because
	the new node will create itself with a reference count of 1, and then
	will have another reference added by its parent scene node when it is
	added to the scene, I need to drop my reference to it. Best practice is
	to drop it only *after* I have finished using it, regardless of what
	the reference count of the object is after creation.
	\code
	CSampleSceneNode *myNode =
		new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);

	\endcode

	To animate something in this boring scene consisting only of one
	tetraeder, and to show that you now can use your scene node like any
	other scene node in the engine, we add an animator to the scene node,
	which rotates the node a little bit.
	irr::scene::ISceneManager::createRotationAnimator() could return 0, so
	should be checked.
	\code
	scene::ISceneNodeAnimator* anim =
		smgr->createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));

	if(anim)
	{
		myNode->addAnimator(anim);
		
		\endcode

		I'm done referring to anim, so must
		irr::IReferenceCounted::drop() this reference now because it
		was produced by a createFoo() function. As I shouldn't refer to
		it again, ensure that I can't by setting to 0.
		\code
		anim->drop();
		anim = 0;
	}

	\endcode

	I'm done with my CSampleSceneNode object, and so must drop my reference.
	This won't delete the object, yet, because it is still attached to the
	scene graph, which prevents the deletion until the graph is deleted or the
	custom scene node is removed from it.
	\code
	myNode->drop();
	myNode = 0; // As I shouldn't refer to it again, ensure that I can't

	\endcode

	Now draw everything and finish.
	\code
	u32 frames=0;
	while(device->run())
	{
		driver->beginScene(true, true, video::SColor(0,100,100,100));

		smgr->drawAll();

		driver->endScene();
		if (++frames==100)
		{
			core::stringw str = L"Irrlicht Engine [";
			str += driver->getName();
			str += L"] FPS: ";
			str += (s32)driver->getFPS();

			device->setWindowCaption(str.c_str());
			frames=0;
		}
	}

	device->drop();
	
	return 0;
}

\endcode

That's it. Compile and play around with the program.
**/
/** \page example004 Tutorial 4: Movement
 \image html "004shot.jpg"
 \image latex "004shot.jpg"

This Tutorial shows how to move and animate SceneNodes. The
basic concept of SceneNodeAnimators is shown as well as manual
movement of nodes using the keyboard.  We'll demonstrate framerate
independent movement, which means moving by an amount dependent
on the duration of the last run of the Irrlicht loop.

Example 19.MouseAndJoystick shows how to handle those kinds of input.

As always, I include the header files, use the irr namespace,
and tell the linker to link with the .lib file.
\code
#ifdef _MSC_VER
// We'll also define this to stop MSVC complaining about sprintf().
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "Irrlicht.lib")
#endif

#include <irrlicht.h>
#include <iostream>

using namespace irr;

\endcode

To receive events like mouse and keyboard input, or GUI events like "the OK
button has been clicked", we need an object which is derived from the
irr::IEventReceiver object. There is only one method to override:
irr::IEventReceiver::OnEvent(). This method will be called by the engine once
when an event happens. What we really want to know is whether a key is being
held down, and so we will remember the current state of each key.
\code
class MyEventReceiver : public IEventReceiver
{
public:
	// This is the one method that we have to implement
	virtual bool OnEvent(const SEvent& event)
	{
		// Remember whether each key is down or up
		if (event.EventType == irr::EET_KEY_INPUT_EVENT)
			KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

		return false;
	}

	// This is used to check whether a key is being held down
	virtual bool IsKeyDown(EKEY_CODE keyCode) const
	{
		return KeyIsDown[keyCode];
	}
	
	MyEventReceiver()
	{
		for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
			KeyIsDown[i] = false;
	}

private:
	// We use this array to store the current state of each key
	bool KeyIsDown[KEY_KEY_CODES_COUNT];
};


\endcode

The event receiver for keeping the pressed keys is ready, the actual responses
will be made inside the render loop, right before drawing the scene. So lets
just create an irr::IrrlichtDevice and the scene node we want to move. We also
create some other additional scene nodes, to show that there are also some
different possibilities to move and animate scene nodes.
\code
int main()
{
	// let user select driver type

	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 0;
	}	

	// create device
	MyEventReceiver receiver;

	IrrlichtDevice* device = createDevice(driverType,
			core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);

	if (device == 0)
		return 1; // could not create selected driver.

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

	\endcode

	Create the node which will be moved with the WSAD keys. We create a
	sphere node, which is a built-in geometry primitive. We place the node
	at (0,0,30) and assign a texture to it to let it look a little bit more
	interesting. Because we have no dynamic lights in this scene we disable
	lighting for each model (otherwise the models would be black).
	\code
	scene::ISceneNode * node = smgr->addSphereSceneNode();
	if (node)
	{
		node->setPosition(core::vector3df(0,0,30));
		node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
		node->setMaterialFlag(video::EMF_LIGHTING, false);
	}

	\endcode

	Now we create another node, movable using a scene node animator. Scene
	node animators modify scene nodes and can be attached to any scene node
	like mesh scene nodes, billboards, lights and even camera scene nodes.
	Scene node animators are not only able to modify the position of a
	scene node, they can also animate the textures of an object for
	example. We create a cube scene node and attach a 'fly circle' scene
	node animator to it, letting this node fly around our sphere scene node.
	\code
	scene::ISceneNode* n = smgr->addCubeSceneNode();

	if (n)
	{
		n->setMaterialTexture(0, driver->getTexture("../../media/t351sml.jpg"));
		n->setMaterialFlag(video::EMF_LIGHTING, false);
		scene::ISceneNodeAnimator* anim =
			smgr->createFlyCircleAnimator(core::vector3df(0,0,30), 20.0f);
		if (anim)
		{
			n->addAnimator(anim);
			anim->drop();
		}
	}

	\endcode

	The last scene node we add to show possibilities of scene node animators is
	a b3d model, which uses a 'fly straight' animator to run between to points.
	\code
	scene::IAnimatedMeshSceneNode* anms =
		smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"));

	if (anms)
	{
		scene::ISceneNodeAnimator* anim =
			smgr->createFlyStraightAnimator(core::vector3df(100,0,60),
			core::vector3df(-100,0,60), 3500, true);
		if (anim)
		{
			anms->addAnimator(anim);
			anim->drop();
		}

		\endcode

		To make the model look right we disable lighting, set the
		frames between which the animation should loop, rotate the
		model around 180 degrees, and adjust the animation speed and
		the texture. To set the right animation (frames and speed), we
		would also be able to just call
		"anms->setMD2Animation(scene::EMAT_RUN)" for the 'run'
		animation instead of "setFrameLoop" and "setAnimationSpeed",
		but this only works with MD2 animations, and so you know how to
		start other animations. But a good advice is to not use
		hardcoded frame-numbers...
		\code
		anms->setMaterialFlag(video::EMF_LIGHTING, false);

		anms->setFrameLoop(0, 13);
		anms->setAnimationSpeed(15);
//		anms->setMD2Animation(scene::EMAT_RUN);

		anms->setScale(core::vector3df(2.f,2.f,2.f));
		anms->setRotation(core::vector3df(0,-90,0));
//		anms->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));

	}


	\endcode

	To be able to look at and move around in this scene, we create a first
	person shooter style camera and make the mouse cursor invisible.
	\code
	smgr->addCameraSceneNodeFPS();
	device->getCursorControl()->setVisible(false);

	\endcode

	Add a colorful irrlicht logo
	\code
	device->getGUIEnvironment()->addImage(
		driver->getTexture("../../media/irrlichtlogoalpha2.tga"),
		core::position2d<s32>(10,20));

	gui::IGUIStaticText* diagnostics = device->getGUIEnvironment()->addStaticText(
		L"", core::rect<s32>(10, 10, 400, 20));
	diagnostics->setOverrideColor(video::SColor(255, 255, 255, 0));

	\endcode

	We have done everything, so lets draw it. We also write the current
	frames per second and the name of the driver to the caption of the
	window.
	\code
	int lastFPS = -1;

	// In order to do framerate independent movement, we have to know
	// how long it was since the last frame
	u32 then = device->getTimer()->getTime();

	// This is the movemen speed in units per second.
	const f32 MOVEMENT_SPEED = 5.f;

	while(device->run())
	{
		// Work out a frame delta time.
		const u32 now = device->getTimer()->getTime();
		const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
		then = now;

		\endcode  Check if keys W, S, A or D are being held down, and move the
		sphere node around respectively. \code
		core::vector3df nodePosition = node->getPosition();

		if(receiver.IsKeyDown(irr::KEY_KEY_W))
			nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
		else if(receiver.IsKeyDown(irr::KEY_KEY_S))
			nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;

		if(receiver.IsKeyDown(irr::KEY_KEY_A))
			nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
		else if(receiver.IsKeyDown(irr::KEY_KEY_D))
			nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;

		node->setPosition(nodePosition);

		driver->beginScene(true, true, video::SColor(255,113,113,133));

		smgr->drawAll(); // draw the 3d scene
		device->getGUIEnvironment()->drawAll(); // draw the gui environment (the logo)

		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps)
		{
			core::stringw tmp(L"Movement Example - Irrlicht Engine [");
			tmp += driver->getName();
			tmp += L"] fps: ";
			tmp += fps;

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

	\endcode

	In the end, delete the Irrlicht device.
	\code
	device->drop();
	
	return 0;
}

\endcode

That's it. Compile and play around with the program.
**/
/** \page example005 Tutorial 5: User Interface
 \image html "005shot.jpg"
 \image latex "005shot.jpg"

This tutorial shows how to use the built in User Interface of
the Irrlicht Engine. It will give a brief overview and show
how to create and use windows, buttons, scroll bars, static
texts, and list boxes.

As always, we include the header files, and use the irrlicht
namespaces. We also store a pointer to the Irrlicht device,
a counter variable for changing the creation position of a window,
and a pointer to a listbox.
\code
#include <irrlicht.h>
#include <iostream>

using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif

// Declare a structure to hold some context for the event receiver so that it
// has it available inside its OnEvent() method.
struct SAppContext
{
	IrrlichtDevice *device;
	s32				counter;
	IGUIListBox*	listbox;
};

// Define some values that we'll use to identify individual GUI controls.
enum
{
	GUI_ID_QUIT_BUTTON = 101,
	GUI_ID_NEW_WINDOW_BUTTON,
	GUI_ID_FILE_OPEN_BUTTON,
	GUI_ID_TRANSPARENCY_SCROLL_BAR
};

\endcode

The Event Receiver is not only capable of getting keyboard and
mouse input events, but also events of the graphical user interface
(gui). There are events for almost everything: Button click,
Listbox selection change, events that say that a element was hovered
and so on. To be able to react to some of these events, we create
an event receiver.
We only react to gui events, and if it's such an event, we get the
id of the caller (the gui element which caused the event) and get
the pointer to the gui environment.
\code
class MyEventReceiver : public IEventReceiver
{
public:
	MyEventReceiver(SAppContext & context) : Context(context) { }

	virtual bool OnEvent(const SEvent& event)
	{
		if (event.EventType == EET_GUI_EVENT)
		{
			s32 id = event.GUIEvent.Caller->getID();
			IGUIEnvironment* env = Context.device->getGUIEnvironment();

			switch(event.GUIEvent.EventType)
			{

			\endcode

			If a scrollbar changed its scroll position, and it is
			'our' scrollbar (the one with id GUI_ID_TRANSPARENCY_SCROLL_BAR), then we change
			the transparency of all gui elements. This is a very
			easy task: There is a skin object, in which all color
			settings are stored. We simply go through all colors
			stored in the skin and change their alpha value.
			\code
			case EGET_SCROLL_BAR_CHANGED:
				if (id == GUI_ID_TRANSPARENCY_SCROLL_BAR)
				{
					s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
					
					for (u32 i=0; i<EGDC_COUNT ; ++i)
					{
						SColor col = env->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
						col.setAlpha(pos);
						env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
					}
					
				}
				break;

			\endcode

			If a button was clicked, it could be one of 'our'
			three buttons. If it is the first, we shut down the engine.
			If it is the second, we create a little window with some
			text on it. We also add a string to the list box to log
			what happened. And if it is the third button, we create
			a file open dialog, and add also this as string to the list box.
			That's all for the event receiver.
			\code
			case EGET_BUTTON_CLICKED:
				switch(id)
				{
				case GUI_ID_QUIT_BUTTON:
					Context.device->closeDevice();
					return true;

				case GUI_ID_NEW_WINDOW_BUTTON:
					{
					Context.listbox->addItem(L"Window created");
					Context.counter += 30;
					if (Context.counter > 200)
						Context.counter = 0;

					IGUIWindow* window = env->addWindow(
						rect<s32>(100 + Context.counter, 100 + Context.counter, 300 + Context.counter, 200 + Context.counter),
						false, // modal?
						L"Test window");

					env->addStaticText(L"Please close me",
						rect<s32>(35,35,140,50),
						true, // border?
						false, // wordwrap?
						window);
					}
					return true;

				case GUI_ID_FILE_OPEN_BUTTON:
					Context.listbox->addItem(L"File open");
					env->addFileOpenDialog(L"Please choose a file.");
					return true;

				default:
					return false;
				}
				break;

			default:
				break;
			}
		}

		return false;
	}

private:
	SAppContext & Context;
};


\endcode

Ok, now for the more interesting part. First, create the Irrlicht device. As in
some examples before, we ask the user which driver he wants to use for this
example:
\code
int main()
{
	// ask user for driver
	
	video::E_DRIVER_TYPE driverType;

	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;
	}	

	// create device and exit if creation failed

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

	if (device == 0)
		return 1; // could not create selected driver.

	\endcode  The creation was successful, now we set the event receiver and
		store pointers to the driver and to the gui environment. \code

	device->setWindowCaption(L"Irrlicht Engine - User Interface Demo");
	device->setResizable(true);

	video::IVideoDriver* driver = device->getVideoDriver();
	IGUIEnvironment* env = device->getGUIEnvironment();

	\endcode

	To make the font a little bit nicer, we load an external font
	and set it as the new default font in the skin.
	To keep the standard font for tool tip text, we set it to
	the built-in font.
	\code

	IGUISkin* skin = env->getSkin();
	IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
	if (font)
		skin->setFont(font);

	skin->setFont(env->getBuiltInFont(), EGDF_TOOLTIP);

	\endcode

	We add three buttons. The first one closes the engine. The second
	creates a window and the third opens a file open dialog. The third
	parameter is the id of the button, with which we can easily identify
	the button in the event receiver.
	\code	

	env->addButton(rect<s32>(10,240,110,240 + 32), 0, GUI_ID_QUIT_BUTTON,
			L"Quit", L"Exits Program");
	env->addButton(rect<s32>(10,280,110,280 + 32), 0, GUI_ID_NEW_WINDOW_BUTTON,
			L"New Window", L"Launches a new Window");
	env->addButton(rect<s32>(10,320,110,320 + 32), 0, GUI_ID_FILE_OPEN_BUTTON,
			L"File Open", L"Opens a file");

	\endcode

	Now, we add a static text and a scrollbar, which modifies the
	transparency of all gui elements. We set the maximum value of
	the scrollbar to 255, because that's the maximal value for
	a color value.
	Then we create an other static text and a list box.
	\code

	env->addStaticText(L"Transparent Control:", rect<s32>(150,20,350,40), true);
	IGUIScrollBar* scrollbar = env->addScrollBar(true,
			rect<s32>(150, 45, 350, 60), 0, GUI_ID_TRANSPARENCY_SCROLL_BAR);
	scrollbar->setMax(255);

	// set scrollbar position to alpha value of an arbitrary element
	scrollbar->setPos(env->getSkin()->getColor(EGDC_WINDOW).getAlpha());

	env->addStaticText(L"Logging ListBox:", rect<s32>(50,110,250,130), true);
	IGUIListBox * listbox = env->addListBox(rect<s32>(50, 140, 250, 210));
	env->addEditBox(L"Editable Text", rect<s32>(350, 80, 550, 100));

	// Store the appropriate data in a context structure.
	SAppContext context;
	context.device = device;
	context.counter = 0;
	context.listbox = listbox;

	// Then create the event receiver, giving it that context structure.
	MyEventReceiver receiver(context);

	// And tell the device to use our custom event receiver.
	device->setEventReceiver(&receiver);


	\endcode

	And at last, we create a nice Irrlicht Engine logo in the top left corner. 
	\code
	env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
			position2d<int>(10,10));


	\endcode

	That's all, we only have to draw everything.
	\code

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

		env->drawAll();
	
		driver->endScene();
	}

	device->drop();

	return 0;
}

\endcode

**/
/** \page example006 Tutorial 6: 2D Graphics
 \image html "006shot.jpg"
 \image latex "006shot.jpg"

This Tutorial shows how to do 2d graphics with the Irrlicht Engine.
It shows how to draw images, keycolor based sprites,
transparent rectangles, and different fonts. You may consider
this useful if you want to make a 2d game with the engine, or if
you want to draw a cool interface or head up display for your 3d game.

As always, I include the header files, use the irr namespace,
and tell the linker to link with the .lib file.
\code
#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

\endcode

At first, we let the user select the driver type, then start up the engine, set
a caption, and get a pointer to the video driver.
\code
int main()
{
	// let user select driver type

	video::E_DRIVER_TYPE driverType;

	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 0;
	}

	// create device

	IrrlichtDevice *device = createDevice(driverType,
		core::dimension2d<u32>(512, 384));

	if (device == 0)
		return 1; // could not create selected driver.

	device->setWindowCaption(L"Irrlicht Engine - 2D Graphics Demo");

	video::IVideoDriver* driver = device->getVideoDriver();

	\endcode

	All 2d graphics in this example are put together into one texture,
	2ddemo.png. Because we want to draw colorkey based sprites, we need to
	load this texture and tell the engine, which part of it should be
	transparent based on a colorkey.

	In this example, we don't tell it the color directly, we just say "Hey
	Irrlicht Engine, you'll find the color I want at position (0,0) on the
	texture.". Instead, it would be also possible to call
	driver->makeColorKeyTexture(images, video::SColor(0,0,0,0)), to make
	e.g. all black pixels transparent. Please note that
	makeColorKeyTexture just creates an alpha channel based on the color.
	\code
	video::ITexture* images = driver->getTexture("../../media/2ddemo.png");
	driver->makeColorKeyTexture(images, core::position2d<s32>(0,0));

	\endcode

	To be able to draw some text with two different fonts, we first load
	them. Ok, we load just one. As the first font we just use the default
	font which is built into the engine. Also, we define two rectangles
	which specify the position of the images of the red imps (little flying
	creatures) in the texture.
	\code
	gui::IGUIFont* font = device->getGUIEnvironment()->getBuiltInFont();
	gui::IGUIFont* font2 =
		device->getGUIEnvironment()->getFont("../../media/fonthaettenschweiler.bmp");

	core::rect<s32> imp1(349,15,385,78);
	core::rect<s32> imp2(387,15,423,78);

	\endcode

	Prepare a nicely filtering 2d render mode for special cases.
	\code
	driver->getMaterial2D().TextureLayer[0].BilinearFilter=true;
	driver->getMaterial2D().AntiAliasing=video::EAAM_FULL_BASIC;

	\endcode

	Everything is prepared, now we can draw everything in the draw loop,
	between the begin scene and end scene calls. In this example, we are
	just doing 2d graphics, but it would be no problem to mix them with 3d
	graphics. Just try it out, and draw some 3d vertices or set up a scene
	with the scene manager and draw it.
	\code
	while(device->run() && driver)
	{
		if (device->isWindowActive())
		{
			u32 time = device->getTimer()->getTime();

			driver->beginScene(true, true, video::SColor(255,120,102,136));

			\endcode

			First, we draw 3 sprites, using the alpha channel we
			created with makeColorKeyTexture. The last parameter
			specifies that the drawing method should use this alpha
			channel. The last-but-one parameter specifies a
			color, with which the sprite should be colored.
			(255,255,255,255) is full white, so the sprite will
			look like the original. The third sprite is drawn
			with the red channel modulated based on the time.
			\code

			// draw fire & dragons background world
			driver->draw2DImage(images, core::position2d<s32>(50,50),
				core::rect<s32>(0,0,342,224), 0,
				video::SColor(255,255,255,255), true);

			// draw flying imp
			driver->draw2DImage(images, core::position2d<s32>(164,125),
				(time/500 % 2) ? imp1 : imp2, 0,
				video::SColor(255,255,255,255), true);

			// draw second flying imp with colorcylce
			driver->draw2DImage(images, core::position2d<s32>(270,105),
				(time/500 % 2) ? imp1 : imp2, 0,
				video::SColor(255,(time) % 255,255,255), true);

			\endcode

			Drawing text is really simple. The code should be self
			explanatory.
			\code

			// draw some text
			if (font)
				font->draw(L"This demo shows that Irrlicht is also capable of drawing 2D graphics.",
					core::rect<s32>(130,10,300,50),
					video::SColor(255,255,255,255));

			// draw some other text
			if (font2)
				font2->draw(L"Also mixing with 3d graphics is possible.",
					core::rect<s32>(130,20,300,60),
					video::SColor(255,time % 255,time % 255,255));

			\endcode

			Next, we draw the Irrlicht Engine logo (without
			using a color or an alpha channel). Since we slightly scale
			the image we use the prepared filter mode.
			\code
			driver->enableMaterial2D();
			driver->draw2DImage(images, core::rect<s32>(10,10,108,48),
				core::rect<s32>(354,87,442,118));
			driver->enableMaterial2D(false);

			\endcode

			Finally draw a half-transparent rect under the mouse cursor.
			\code
			core::position2d<s32> m = device->getCursorControl()->getPosition();
			driver->draw2DRectangle(video::SColor(100,255,255,255),
				core::rect<s32>(m.X-20, m.Y-20, m.X+20, m.Y+20));

			driver->endScene();
		}
	}

	device->drop();

	return 0;
}

\endcode

That's all. I hope it was not too difficult.
**/
/** \page example007 Tutorial 7: Collision
 \image html "007shot.jpg"
 \image latex "007shot.jpg"

We will describe 2 methods: Automatic collision detection for moving through 3d worlds
with stair climbing and sliding, and manual scene node and triangle picking using a
ray.  In this case, we will use a ray coming out from the camera, but you can use
any ray.

To start, we take the program from tutorial 2, which loads and displays a quake
3 level. We will use the level to walk in it and to pick triangles from. In
addition we'll place 3 animated models into it for triangle picking. The
following code starts up the engine and loads a quake 3 level, as per tutorial 2.
\code
#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

enum
{
	// I use this ISceneNode ID to indicate a scene node that is
	// not pickable by getSceneNodeAndCollisionPointFromRay()
	ID_IsNotPickable = 0,

	// I use this flag in ISceneNode IDs to indicate that the
	// scene node can be picked by ray selection.
	IDFlag_IsPickable = 1 << 0,

	// I use this flag in ISceneNode IDs to indicate that the
	// scene node can be highlighted.  In this example, the
	// homonids can be highlighted, but the level mesh can't.
	IDFlag_IsHighlightable = 1 << 1
};

int main()
{
	// let user select driver type

	video::E_DRIVER_TYPE driverType;

	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 0;
	}

	// create device

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

	if (device == 0)
		return 1; // could not create selected driver.

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

	device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");

	scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
	scene::IMeshSceneNode* q3node = 0;

	// The Quake mesh is pickable, but doesn't get highlighted.
	if (q3levelmesh)
		q3node = smgr->addOctreeSceneNode(q3levelmesh->getMesh(0), 0, IDFlag_IsPickable);

	\endcode

	So far so good, we've loaded the quake 3 level like in tutorial 2. Now,
	here comes something different: We create a triangle selector. A
	triangle selector is a class which can fetch the triangles from scene
	nodes for doing different things with them, for example collision
	detection. There are different triangle selectors, and all can be
	created with the ISceneManager. In this example, we create an
	OctreeTriangleSelector, which optimizes the triangle output a little
	bit by reducing it like an octree. This is very useful for huge meshes
	like quake 3 levels. After we created the triangle selector, we attach
	it to the q3node. This is not necessary, but in this way, we do not
	need to care for the selector, for example dropping it after we do not
	need it anymore.
	\code

	scene::ITriangleSelector* selector = 0;

	if (q3node)
	{
		q3node->setPosition(core::vector3df(-1350,-130,-1400));

		selector = smgr->createOctreeTriangleSelector(
				q3node->getMesh(), q3node, 128);
		q3node->setTriangleSelector(selector);
		// We're not done with this selector yet, so don't drop it.
	}


	\endcode

	We add a first person shooter camera to the scene so that we can see and
	move in the quake 3 level like in tutorial 2. But this, time, we add a
	special animator to the camera: A Collision Response animator. This
	animator modifies the scene node to which it is attached to in order to
	prevent it moving through walls, and to add gravity to it. The
	only thing we have to tell the animator is how the world looks like,
	how big the scene node is, how much gravity to apply and so on. After the
	collision response animator is attached to the camera, we do not have to do
	anything more for collision detection, anything is done automatically.
	The rest of the collision detection code below is for picking. And please
	note another cool feature: The collision response animator can be
	attached also to all other scene nodes, not only to cameras. And it can
	be mixed with other scene node animators. In this way, collision
	detection and response in the Irrlicht engine is really easy.

	Now we'll take a closer look on the parameters of
	createCollisionResponseAnimator(). The first parameter is the
	TriangleSelector, which specifies how the world, against collision
	detection is done looks like. The second parameter is the scene node,
	which is the object, which is affected by collision detection, in our
	case it is the camera. The third defines how big the object is, it is
	the radius of an ellipsoid. Try it out and change the radius to smaller
	values, the camera will be able to move closer to walls after this. The
	next parameter is the direction and speed of gravity.  We'll set it to
	(0, -10, 0), which approximates to realistic gravity, assuming that our
	units are metres. You could set it to (0,0,0) to disable gravity. And the
	last value is just a translation: Without this, the ellipsoid with which
	collision detection is done would be around the camera, and the camera would
	be in the middle of the ellipsoid. But as human beings, we are used to have our
	eyes on top of the body, with which we collide with our world, not in
	the middle of it. So we place the scene node 50 units over the center
	of the ellipsoid with this parameter. And that's it, collision
	detection works now.
	\code

	// Set a jump speed of 3 units per second, which gives a fairly realistic jump
	// when used with the gravity of (0, -10, 0) in the collision response animator.
	scene::ICameraSceneNode* camera =
		smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, ID_IsNotPickable, 0, 0, true, 3.f);
	camera->setPosition(core::vector3df(50,50,-60));
	camera->setTarget(core::vector3df(-70,30,-60));

	if (selector)
	{
		scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
			selector, camera, core::vector3df(30,50,30),
			core::vector3df(0,-10,0), core::vector3df(0,30,0));
		selector->drop(); // As soon as we're done with the selector, drop it.
		camera->addAnimator(anim);
		anim->drop();  // And likewise, drop the animator when we're done referring to it.
	}

	// Now I create three animated characters which we can pick, a dynamic light for
	// lighting them, and a billboard for drawing where we found an intersection.

	// First, let's get rid of the mouse cursor.  We'll use a billboard to show
	// what we're looking at.
	device->getCursorControl()->setVisible(false);

	// Add the billboard.
	scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
	bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
	bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
	bill->setMaterialFlag(video::EMF_LIGHTING, false);
	bill->setMaterialFlag(video::EMF_ZBUFFER, false);
	bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));
	bill->setID(ID_IsNotPickable); // This ensures that we don't accidentally ray-pick it

	\endcode  Add 3 animated hominids, which we can pick using a ray-triangle intersection.
	They all animate quite slowly, to make it easier to see that accurate triangle
	selection is being performed. \code
	scene::IAnimatedMeshSceneNode* node = 0;

	// Add an MD2 node, which uses vertex-based animation.
	node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/faerie.md2"),
						0, IDFlag_IsPickable | IDFlag_IsHighlightable);
	node->setPosition(core::vector3df(-70,-15,-120)); // Put its feet on the floor.
	node->setScale(core::vector3df(2, 2, 2)); // Make it appear realistically scaled
	node->setMD2Animation(scene::EMAT_POINT);
	node->setAnimationSpeed(20.f);
	video::SMaterial material;
	material.setTexture(0, driver->getTexture("../../media/faerie2.bmp"));
	material.Lighting = true;
	material.NormalizeNormals = true;
	node->getMaterial(0) = material;

	// Now create a triangle selector for it.  The selector will know that it
	// is associated with an animated node, and will update itself as necessary.
	selector = smgr->createTriangleSelector(node);
	node->setTriangleSelector(selector);
	selector->drop(); // We're done with this selector, so drop it now.

	// This X files uses skeletal animation, but without skinning.
	node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/dwarf.x"),
						0, IDFlag_IsPickable | IDFlag_IsHighlightable);
	node->setPosition(core::vector3df(-70,-66,0)); // Put its feet on the floor.
	node->setRotation(core::vector3df(0,-90,0)); // And turn it towards the camera.
	node->setAnimationSpeed(20.f);
	selector = smgr->createTriangleSelector(node);
	node->setTriangleSelector(selector);
	selector->drop();

	// And this B3D file uses skinned skeletal animation.
	node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"),
						0, IDFlag_IsPickable | IDFlag_IsHighlightable);
	node->setScale(core::vector3df(10, 10, 10));
	node->setPosition(core::vector3df(-70,-66,-60));
	node->setRotation(core::vector3df(0,90,0));
	node->setAnimationSpeed(10.f);
	node->getMaterial(0).NormalizeNormals = true;
	// Just do the same as we did above.
	selector = smgr->createTriangleSelector(node);
	node->setTriangleSelector(selector);
	selector->drop();

	material.setTexture(0, 0);
	material.Lighting = false;

	// Add a light, so that the unselected nodes aren't completely dark.
	scene::ILightSceneNode * light = smgr->addLightSceneNode(0, core::vector3df(-60,100,400),
		video::SColorf(1.0f,1.0f,1.0f,1.0f), 600.0f);
	light->setID(ID_IsNotPickable); // Make it an invalid target for selection.

	// Remember which scene node is highlighted
	scene::ISceneNode* highlightedSceneNode = 0;
	scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
	int lastFPS = -1;

	// draw the selection triangle only as wireframe
	material.Wireframe=true;

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

		// Unlight any currently highlighted scene node
		if (highlightedSceneNode)
		{
			highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, true);
			highlightedSceneNode = 0;
		}

		// All intersections in this example are done with a ray cast out from the camera to
		// a distance of 1000.  You can easily modify this to check (e.g.) a bullet
		// trajectory or a sword's position, or create a ray from a mouse click position using
		// ISceneCollisionManager::getRayFromScreenCoordinates()
		core::line3d<f32> ray;
		ray.start = camera->getPosition();
		ray.end = ray.start + (camera->getTarget() - ray.start).normalize() * 1000.0f;

		// Tracks the current intersection point with the level or a mesh
		core::vector3df intersection;
		// Used to show with triangle has been hit
		core::triangle3df hitTriangle;

		// This call is all you need to perform ray/triangle collision on every scene node
		// that has a triangle selector, including the Quake level mesh.  It finds the nearest
		// collision point/triangle, and returns the scene node containing that point.
		// Irrlicht provides other types of selection, including ray/triangle selector,
		// ray/box and ellipse/triangle selector, plus associated helpers.
		// See the methods of ISceneCollisionManager
		scene::ISceneNode * selectedSceneNode =
			collMan->getSceneNodeAndCollisionPointFromRay(
					ray,
					intersection, // This will be the position of the collision
					hitTriangle, // This will be the triangle hit in the collision
					IDFlag_IsPickable, // This ensures that only nodes that we have
							// set up to be pickable are considered
					0); // Check the entire scene (this is actually the implicit default)

		// If the ray hit anything, move the billboard to the collision position
		// and draw the triangle that was hit.
		if(selectedSceneNode)
		{
			bill->setPosition(intersection);

			// We need to reset the transform before doing our own rendering.
			driver->setTransform(video::ETS_WORLD, core::matrix4());
			driver->setMaterial(material);
			driver->draw3DTriangle(hitTriangle, video::SColor(0,255,0,0));

			// We can check the flags for the scene node that was hit to see if it should be
			// highlighted. The animated nodes can be highlighted, but not the Quake level mesh
			if((selectedSceneNode->getID() & IDFlag_IsHighlightable) == IDFlag_IsHighlightable)
			{
				highlightedSceneNode = selectedSceneNode;

				// Highlighting in this case means turning lighting OFF for this node,
				// which means that it will be drawn with full brightness.
				highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, false);
			}
		}

		// We're all done drawing, so end the scene.
		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps)
		{
			core::stringw str = L"Collision detection example - Irrlicht Engine [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;

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

	device->drop();

	return 0;
}

\endcode

**/
/** \page example008 Tutorial 8: SpecialFX
 \image html "008shot.jpg"
 \image latex "008shot.jpg"

This tutorials describes how to do special effects. It shows how to use stencil
buffer shadows, the particle system, billboards, dynamic light, and the water
surface scene node.

We start like in some tutorials before. Please note that this time, the
'shadows' flag in createDevice() is set to true, for we want to have a dynamic
shadow casted from an animated character. If this example runs too slow,
set it to false. The Irrlicht Engine checks if your hardware doesn't support
the stencil buffer, and disables shadows by itself, but just in case the demo
runs slow on your hardware.
\code

#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

int main()
{
	// ask if user would like shadows

	char i;
	printf("Please press 'y' if you want to use realtime shadows.\n");

	std::cin >> i;

	const bool shadows = (i == 'y');

	// ask user for driver

	video::E_DRIVER_TYPE driverType;

	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");

	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;
	}

	\endcode

	Create device and exit if creation failed. We make the stencil flag
	optional to avoid slow screen modes for runs without shadows.
	\code

	IrrlichtDevice *device =
		createDevice(driverType, core::dimension2d<u32>(640, 480),
		16, false, shadows);

	if (device == 0)
		return 1; // could not create selected driver.

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

	\endcode

	For our environment, we load a .3ds file. It is a small room I modelled
	with Anim8or and exported into the 3ds format because the Irrlicht
	Engine does not support the .an8 format. I am a very bad 3d graphic
	artist, and so the texture mapping is not very nice in this model.
	Luckily I am a better programmer than artist, and so the Irrlicht
	Engine is able to create a cool texture mapping for me: Just use the
	mesh manipulator and create a planar texture mapping for the mesh. If
	you want to see the mapping I made with Anim8or, uncomment this line. I
	also did not figure out how to set the material right in Anim8or, it
	has a specular light color which I don't really like. I'll switch it
	off too with this code.
	\code

	scene::IAnimatedMesh* mesh = smgr->getMesh("../../media/room.3ds");

	smgr->getMeshManipulator()->makePlanarTextureMapping(mesh->getMesh(0), 0.004f);

	scene::ISceneNode* node = 0;

	node = smgr->addAnimatedMeshSceneNode(mesh);
	node->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
	node->getMaterial(0).SpecularColor.set(0,0,0,0);

	\endcode

	Now, for the first special effect: Animated water. It works like this:
	The WaterSurfaceSceneNode takes a mesh as input and makes it wave like
	a water surface. And if we let this scene node use a nice material like
	the EMT_REFLECTION_2_LAYER, it looks really cool. We are doing this
	with the next few lines of code. As input mesh, we create a hill plane
	mesh, without hills. But any other mesh could be used for this, you
	could even use the room.3ds (which would look really strange) if you
	want to.
	\code

	mesh = smgr->addHillPlaneMesh( "myHill",
		core::dimension2d<f32>(20,20),
		core::dimension2d<u32>(40,40), 0, 0,
		core::dimension2d<f32>(0,0),
		core::dimension2d<f32>(10,10));

	node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f, 300.0f, 30.0f);
	node->setPosition(core::vector3df(0,7,0));

	node->setMaterialTexture(0, driver->getTexture("../../media/stones.jpg"));
	node->setMaterialTexture(1, driver->getTexture("../../media/water.jpg"));

	node->setMaterialType(video::EMT_REFLECTION_2_LAYER);

	\endcode

	The second special effect is very basic, I bet you saw it already in
	some Irrlicht Engine demos: A transparent billboard combined with a
	dynamic light. We simply create a light scene node, let it fly around,
	and to make it look more cool, we attach a billboard scene node to it.
	\code

	// create light

	node = smgr->addLightSceneNode(0, core::vector3df(0,0,0),
		video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 800.0f);
	scene::ISceneNodeAnimator* anim = 0;
	anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f);
	node->addAnimator(anim);
	anim->drop();

	// attach billboard to light

	node = smgr->addBillboardSceneNode(node, core::dimension2d<f32>(50, 50));
	node->setMaterialFlag(video::EMF_LIGHTING, false);
	node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
	node->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));

	\endcode

	The next special effect is a lot more interesting: A particle system.
	The particle system in the Irrlicht Engine is quite modular and
	extensible, but yet easy to use. There is a particle system scene node
	into which you can put a particle emitter, which makes particles come out
	of nothing. These emitters are quite flexible and usually have lots of
	parameters like direction, amount, and color of the particles they
	create.

	There are different emitters, for example a point emitter which lets
	particles pop out at a fixed point. If the particle emitters available
	in the engine are not enough for you, you can easily create your own
	ones, you'll simply have to create a class derived from the
	IParticleEmitter interface and attach it to the particle system using
	setEmitter(). In this example we create a box particle emitter, which
	creates particles randomly inside a box. The parameters define the box,
	direction of the particles, minimal and maximal new particles per
	second, color, and minimal and maximal lifetime of the particles.

	Because only with emitters particle system would be a little bit
	boring, there are particle affectors which modify particles while
	they fly around. Affectors can be added to a particle system for
	simulating additional effects like gravity or wind.
	The particle affector we use in this example is an affector which
	modifies the color of the particles: It lets them fade out. Like the
	particle emitters, additional particle affectors can also be
	implemented by you, simply derive a class from IParticleAffector and
	add it with addAffector().

	After we set a nice material to the particle system, we have a cool
	looking camp fire. By adjusting material, texture, particle emitter,
	and affector parameters, it is also easily possible to create smoke,
	rain, explosions, snow, and so on.
	\code

	// create a particle system

	scene::IParticleSystemSceneNode* ps =
		smgr->addParticleSystemSceneNode(false);

	scene::IParticleEmitter* em = ps->createBoxEmitter(
		core::aabbox3d<f32>(-7,0,-7,7,1,7), // emitter size
		core::vector3df(0.0f,0.06f,0.0f),   // initial direction
		80,100,                             // emit rate
		video::SColor(0,255,255,255),       // darkest color
		video::SColor(0,255,255,255),       // brightest color
		800,2000,0,                         // min and max age, angle
		core::dimension2df(10.f,10.f),         // min size
		core::dimension2df(20.f,20.f));        // max size

	ps->setEmitter(em); // this grabs the emitter
	em->drop(); // so we can drop it here without deleting it

	scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();

	ps->addAffector(paf); // same goes for the affector
	paf->drop();

	ps->setPosition(core::vector3df(-70,60,40));
	ps->setScale(core::vector3df(2,2,2));
	ps->setMaterialFlag(video::EMF_LIGHTING, false);
	ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
	ps->setMaterialTexture(0, driver->getTexture("../../media/fire.bmp"));
	ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);

	\endcode

	Next we add a volumetric light node, which adds a glowing fake area light to
	the scene. Like with the billboards and particle systems we also assign a
	texture for the desired effect, though this time we'll use a texture animator
	to create the illusion of a magical glowing area effect.
	\code
	scene::IVolumeLightSceneNode * n = smgr->addVolumeLightSceneNode(0, -1,
				32,                              // Subdivisions on U axis
				32,                              // Subdivisions on V axis
				video::SColor(0, 255, 255, 255), // foot color
				video::SColor(0, 0, 0, 0));      // tail color

	if (n)
	{
		n->setScale(core::vector3df(56.0f, 56.0f, 56.0f));
		n->setPosition(core::vector3df(-120,50,40));

		// load textures for animation
		core::array<video::ITexture*> textures;
		for (s32 g=7; g > 0; --g)
		{
			core::stringc tmp;
			tmp = "../../media/portal";
			tmp += g;
			tmp += ".bmp";
			video::ITexture* t = driver->getTexture( tmp.c_str() );
			textures.push_back(t);
		}

		// create texture animator
		scene::ISceneNodeAnimator* glow = smgr->createTextureAnimator(textures, 150);

		// add the animator
		n->addAnimator(glow);

		// drop the animator because it was created with a create() function
		glow->drop();
	}

	\endcode

	As our last special effect, we want a dynamic shadow be casted from an
	animated character. For this we load a DirectX .x model and place it
	into our world. For creating the shadow, we simply need to call
	addShadowVolumeSceneNode(). The color of shadows is only adjustable
	globally for all shadows, by calling ISceneManager::setShadowColor().
	Voila, here is our dynamic shadow.

	Because the character is a little bit too small for this scene, we make
	it bigger using setScale(). And because the character is lighted by a
	dynamic light, we need to normalize the normals to make the lighting on
	it correct. This is always necessary if the scale of a dynamic lighted
	model is not (1,1,1). Otherwise it would get too dark or too bright
	because the normals will be scaled too.
	\code

	// add animated character

	mesh = smgr->getMesh("../../media/dwarf.x");
	scene::IAnimatedMeshSceneNode* anode = 0;

	anode = smgr->addAnimatedMeshSceneNode(mesh);
	anode->setPosition(core::vector3df(-50,20,-60));
	anode->setAnimationSpeed(15);

	// add shadow
	anode->addShadowVolumeSceneNode();
	smgr->setShadowColor(video::SColor(150,0,0,0));

	// make the model a little bit bigger and normalize its normals
	// because of the scaling, for correct lighting
	anode->setScale(core::vector3df(2,2,2));
	anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);

	\endcode

	Finally we simply have to draw everything, that's all.
	\code

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

	// disable mouse cursor
	device->getCursorControl()->setVisible(false);

	s32 lastFPS = -1;

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

		smgr->drawAll();

		driver->endScene();

		const s32 fps = driver->getFPS();

		if (lastFPS != fps)
		{
			core::stringw str = L"Irrlicht Engine - SpecialFX example [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;

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

	device->drop();

	return 0;
}

\endcode

**/
/** \page example009 Tutorial 9: Mesh Viewer
 \image html "009shot.jpg"
 \image latex "009shot.jpg"

This tutorial show how to create a more complex application with the engine.
We construct a simple mesh viewer using the user interface API and the
scene management of Irrlicht. The tutorial show how to create and use Buttons,
Windows, Toolbars, Menus, ComboBoxes, Tabcontrols, Editboxes, Images,
MessageBoxes, SkyBoxes, and how to parse XML files with the integrated XML
reader of the engine.

We start like in most other tutorials: Include all nesessary header files, add
a comment to let the engine be linked with the right .lib file in Visual
Studio, and declare some global variables. We also add two 'using namespace'
statements, so we do not need to write the whole names of all classes. In this
tutorial, we use a lot stuff from the gui namespace.
\code
#include <irrlicht.h>
#include <iostream>

using namespace irr;
using namespace gui;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif


\endcode

Some global variables used later on
\code
IrrlichtDevice *Device = 0;
core::stringc StartUpModelFile;
core::stringw MessageText;
core::stringw Caption;
scene::ISceneNode* Model = 0;
scene::ISceneNode* SkyBox = 0;
bool Octree=false;
bool UseLight=false;

scene::ICameraSceneNode* Camera[2] = {0, 0};

// Values used to identify individual GUI elements
enum
{
	GUI_ID_DIALOG_ROOT_WINDOW  = 0x10000,

	GUI_ID_X_SCALE,
	GUI_ID_Y_SCALE,
	GUI_ID_Z_SCALE,

	GUI_ID_OPEN_MODEL,
	GUI_ID_SET_MODEL_ARCHIVE,
	GUI_ID_LOAD_AS_OCTREE,

	GUI_ID_SKY_BOX_VISIBLE,
	GUI_ID_TOGGLE_DEBUG_INFO,

	GUI_ID_DEBUG_OFF,
	GUI_ID_DEBUG_BOUNDING_BOX,
	GUI_ID_DEBUG_NORMALS,
	GUI_ID_DEBUG_SKELETON,
	GUI_ID_DEBUG_WIRE_OVERLAY,
	GUI_ID_DEBUG_HALF_TRANSPARENT,
	GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES,
	GUI_ID_DEBUG_ALL,

	GUI_ID_MODEL_MATERIAL_SOLID,
	GUI_ID_MODEL_MATERIAL_TRANSPARENT,
	GUI_ID_MODEL_MATERIAL_REFLECTION,

	GUI_ID_CAMERA_MAYA,
	GUI_ID_CAMERA_FIRST_PERSON,

	GUI_ID_POSITION_TEXT,

	GUI_ID_ABOUT,
	GUI_ID_QUIT,

	GUI_ID_TEXTUREFILTER,
	GUI_ID_SKIN_TRANSPARENCY,
	GUI_ID_SKIN_ANIMATION_FPS,

	GUI_ID_BUTTON_SET_SCALE,
	GUI_ID_BUTTON_SCALE_MUL10,
	GUI_ID_BUTTON_SCALE_DIV10,
	GUI_ID_BUTTON_OPEN_MODEL,
	GUI_ID_BUTTON_SHOW_ABOUT,
	GUI_ID_BUTTON_SHOW_TOOLBOX,
	GUI_ID_BUTTON_SELECT_ARCHIVE,

	// And some magic numbers
	MAX_FRAMERATE = 1000,
	DEFAULT_FRAMERATE = 30
};


\endcode

Toggle between various cameras
\code
void setActiveCamera(scene::ICameraSceneNode* newActive)
{
	if (0 == Device)
		return;

	scene::ICameraSceneNode * active = Device->getSceneManager()->getActiveCamera();
	active->setInputReceiverEnabled(false);

	newActive->setInputReceiverEnabled(true);
	Device->getSceneManager()->setActiveCamera(newActive);
}

\endcode

	Set the skin transparency by changing the alpha values of all skin-colors
\code
void SetSkinTransparency(s32 alpha, irr::gui::IGUISkin * skin)
{
	for (s32 i=0; i<irr::gui::EGDC_COUNT ; ++i)
	{
		video::SColor col = skin->getColor((EGUI_DEFAULT_COLOR)i);
		col.setAlpha(alpha);
		skin->setColor((EGUI_DEFAULT_COLOR)i, col);
	}
}

\endcode

  Update the display of the model scaling
\code
void UpdateScaleInfo(scene::ISceneNode* model)
{
	IGUIElement* toolboxWnd = Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
	if (!toolboxWnd)
		return;
	if (!model)
	{
		toolboxWnd->getElementFromId(GUI_ID_X_SCALE, true)->setText( L"-" );
		toolboxWnd->getElementFromId(GUI_ID_Y_SCALE, true)->setText( L"-" );
		toolboxWnd->getElementFromId(GUI_ID_Z_SCALE, true)->setText( L"-" );
	}
	else
	{
		core::vector3df scale = model->getScale();
		toolboxWnd->getElementFromId(GUI_ID_X_SCALE, true)->setText( core::stringw(scale.X).c_str() );
		toolboxWnd->getElementFromId(GUI_ID_Y_SCALE, true)->setText( core::stringw(scale.Y).c_str() );
		toolboxWnd->getElementFromId(GUI_ID_Z_SCALE, true)->setText( core::stringw(scale.Z).c_str() );
	}
}

\endcode

The three following functions do several stuff used by the mesh viewer. The
first function showAboutText() simply displays a messagebox with a caption and
a message text. The texts will be stored in the MessageText and Caption
variables at startup.
\code
void showAboutText()
{
	// create modal message box with the text
	// loaded from the xml file.
	Device->getGUIEnvironment()->addMessageBox(
		Caption.c_str(), MessageText.c_str());
}


\endcode

The second function loadModel() loads a model and displays it using an
addAnimatedMeshSceneNode and the scene manager. Nothing difficult. It also
displays a short message box, if the model could not be loaded.
\code
void loadModel(const c8* fn)
{
	// modify the name if it a .pk3 file

	core::stringc filename(fn);

	core::stringc extension;
	core::getFileNameExtension(extension, filename);
	extension.make_lower();

	// if a texture is loaded apply it to the current model..
	if (extension == ".jpg" || extension == ".pcx" ||
		extension == ".png" || extension == ".ppm" ||
		extension == ".pgm" || extension == ".pbm" ||
		extension == ".psd" || extension == ".tga" ||
		extension == ".bmp" || extension == ".wal" ||
		extension == ".rgb" || extension == ".rgba")
	{
		video::ITexture * texture =
			Device->getVideoDriver()->getTexture( filename );
		if ( texture && Model )
		{
			// always reload texture
			Device->getVideoDriver()->removeTexture(texture);
			texture = Device->getVideoDriver()->getTexture( filename );

			Model->setMaterialTexture(0, texture);
		}
		return;
	}
	// if a archive is loaded add it to the FileArchive..
	else if (extension == ".pk3" || extension == ".zip" || extension == ".pak" || extension == ".npk")
	{
		Device->getFileSystem()->addFileArchive(filename.c_str());
		return;
	}

	// load a model into the engine

	if (Model)
		Model->remove();

	Model = 0;

	if (extension==".irr")
	{
		core::array<scene::ISceneNode*> outNodes;
		Device->getSceneManager()->loadScene(filename);
		Device->getSceneManager()->getSceneNodesFromType(scene::ESNT_ANIMATED_MESH, outNodes);
		if (outNodes.size())
			Model = outNodes[0];
		return;
	}

	scene::IAnimatedMesh* m = Device->getSceneManager()->getMesh( filename.c_str() );

	if (!m)
	{
		// model could not be loaded

		if (StartUpModelFile != filename)
			Device->getGUIEnvironment()->addMessageBox(
			Caption.c_str(), L"The model could not be loaded. " \
			L"Maybe it is not a supported file format.");
		return;
	}

	// set default material properties

	if (Octree)
		Model = Device->getSceneManager()->addOctreeSceneNode(m->getMesh(0));
	else
	{
		scene::IAnimatedMeshSceneNode* animModel = Device->getSceneManager()->addAnimatedMeshSceneNode(m);
		animModel->setAnimationSpeed(30);
		Model = animModel;
	}
	Model->setMaterialFlag(video::EMF_LIGHTING, UseLight);
	Model->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, UseLight);
//	Model->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
	Model->setDebugDataVisible(scene::EDS_OFF);

	// we need to uncheck the menu entries. would be cool to fake a menu event, but
	// that's not so simple. so we do it brute force
	gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId(GUI_ID_TOGGLE_DEBUG_INFO, true);
	if (menu)
		for(int item = 1; item < 6; ++item)
			menu->setItemChecked(item, false);
	UpdateScaleInfo(Model);
}


\endcode

Finally, the third function creates a toolbox window. In this simple mesh
viewer, this toolbox only contains a tab control with three edit boxes for
changing the scale of the displayed model.
\code
void createToolBox()
{
	// remove tool box if already there
	IGUIEnvironment* env = Device->getGUIEnvironment();
	IGUIElement* root = env->getRootGUIElement();
	IGUIElement* e = root->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
	if (e)
		e->remove();

	// create the toolbox window
	IGUIWindow* wnd = env->addWindow(core::rect<s32>(600,45,800,480),
		false, L"Toolset", 0, GUI_ID_DIALOG_ROOT_WINDOW);

	// create tab control and tabs
	IGUITabControl* tab = env->addTabControl(
		core::rect<s32>(2,20,800-602,480-7), wnd, true, true);

	IGUITab* t1 = tab->addTab(L"Config");

	// add some edit boxes and a button to tab one
	env->addStaticText(L"Scale:",
			core::rect<s32>(10,20,60,45), false, false, t1);
	env->addStaticText(L"X:", core::rect<s32>(22,48,40,66), false, false, t1);
	env->addEditBox(L"1.0", core::rect<s32>(40,46,130,66), true, t1, GUI_ID_X_SCALE);
	env->addStaticText(L"Y:", core::rect<s32>(22,82,40,GUI_ID_OPEN_MODEL), false, false, t1);
	env->addEditBox(L"1.0", core::rect<s32>(40,76,130,96), true, t1, GUI_ID_Y_SCALE);
	env->addStaticText(L"Z:", core::rect<s32>(22,108,40,126), false, false, t1);
	env->addEditBox(L"1.0", core::rect<s32>(40,106,130,126), true, t1, GUI_ID_Z_SCALE);

	env->addButton(core::rect<s32>(10,134,85,165), t1, GUI_ID_BUTTON_SET_SCALE, L"Set");

	// quick scale buttons
	env->addButton(core::rect<s32>(65,20,95,40), t1, GUI_ID_BUTTON_SCALE_MUL10, L"* 10");
	env->addButton(core::rect<s32>(100,20,130,40), t1, GUI_ID_BUTTON_SCALE_DIV10, L"* 0.1");

	UpdateScaleInfo(Model);

	// add transparency control
	env->addStaticText(L"GUI Transparency Control:",
			core::rect<s32>(10,200,150,225), true, false, t1);
	IGUIScrollBar* scrollbar = env->addScrollBar(true,
			core::rect<s32>(10,225,150,240), t1, GUI_ID_SKIN_TRANSPARENCY);
	scrollbar->setMax(255);
	scrollbar->setPos(255);

	// add framerate control
	env->addStaticText(L"Framerate:",
			core::rect<s32>(10,240,150,265), true, false, t1);
	scrollbar = env->addScrollBar(true,
			core::rect<s32>(10,265,150,280), t1, GUI_ID_SKIN_ANIMATION_FPS);
	scrollbar->setMax(MAX_FRAMERATE);
	scrollbar->setMin(-MAX_FRAMERATE);
	scrollbar->setPos(DEFAULT_FRAMERATE);

	// bring irrlicht engine logo to front, because it
	// now may be below the newly created toolbox
	root->bringToFront(root->getElementFromId(666, true));
}

\endcode

To get all the events sent by the GUI Elements, we need to create an event
receiver. This one is really simple. If an event occurs, it checks the id of
the caller and the event type, and starts an action based on these values. For
example, if a menu item with id GUI_ID_OPEN_MODEL was selected, if opens a file-open-dialog.
\code
class MyEventReceiver : public IEventReceiver
{
public:
	virtual bool OnEvent(const SEvent& event)
	{
		// Escape swaps Camera Input
		if (event.EventType == EET_KEY_INPUT_EVENT &&
			event.KeyInput.PressedDown == false)
		{
			if ( OnKeyUp(event.KeyInput.Key) )
				return true;
		}

		if (event.EventType == EET_GUI_EVENT)
		{
			s32 id = event.GUIEvent.Caller->getID();
			IGUIEnvironment* env = Device->getGUIEnvironment();

			switch(event.GUIEvent.EventType)
			{
			case EGET_MENU_ITEM_SELECTED:
					// a menu item was clicked
					OnMenuItemSelected( (IGUIContextMenu*)event.GUIEvent.Caller );
				break;

			case EGET_FILE_SELECTED:
				{
					// load the model file, selected in the file open dialog
					IGUIFileOpenDialog* dialog =
						(IGUIFileOpenDialog*)event.GUIEvent.Caller;
					loadModel(core::stringc(dialog->getFileName()).c_str());
				}
				break;

			case EGET_SCROLL_BAR_CHANGED:

				// control skin transparency
				if (id == GUI_ID_SKIN_TRANSPARENCY)
				{
					const s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
					SetSkinTransparency(pos, env->getSkin());
				}
				// control animation speed
				else if (id == GUI_ID_SKIN_ANIMATION_FPS)
				{
					const s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
					if (scene::ESNT_ANIMATED_MESH == Model->getType())
						((scene::IAnimatedMeshSceneNode*)Model)->setAnimationSpeed((f32)pos);
				}
				break;

			case EGET_COMBO_BOX_CHANGED:

				// control anti-aliasing/filtering
				if (id == GUI_ID_TEXTUREFILTER)
				{
					OnTextureFilterSelected( (IGUIComboBox*)event.GUIEvent.Caller );
				}
				break;

			case EGET_BUTTON_CLICKED:

				switch(id)
				{
				case GUI_ID_BUTTON_SET_SCALE:
					{
						// set scale
						gui::IGUIElement* root = env->getRootGUIElement();
						core::vector3df scale;
						core::stringc s;

						s = root->getElementFromId(GUI_ID_X_SCALE, true)->getText();
						scale.X = (f32)atof(s.c_str());
						s = root->getElementFromId(GUI_ID_Y_SCALE, true)->getText();
						scale.Y = (f32)atof(s.c_str());
						s = root->getElementFromId(GUI_ID_Z_SCALE, true)->getText();
						scale.Z = (f32)atof(s.c_str());

						if (Model)
							Model->setScale(scale);
						UpdateScaleInfo(Model);
					}
					break;
				case GUI_ID_BUTTON_SCALE_MUL10:
					if (Model)
						Model->setScale(Model->getScale()*10.f);
					UpdateScaleInfo(Model);
					break;
				case GUI_ID_BUTTON_SCALE_DIV10:
					if (Model)
						Model->setScale(Model->getScale()*0.1f);
					UpdateScaleInfo(Model);
					break;
				case GUI_ID_BUTTON_OPEN_MODEL:
					env->addFileOpenDialog(L"Please select a model file to open");
					break;
				case GUI_ID_BUTTON_SHOW_ABOUT:
					showAboutText();
					break;
				case GUI_ID_BUTTON_SHOW_TOOLBOX:
					createToolBox();
					break;
				case GUI_ID_BUTTON_SELECT_ARCHIVE:
					env->addFileOpenDialog(L"Please select your game archive/directory");
					break;
				}

				break;
			default:
				break;
			}
		}

		return false;
	}


	\endcode

		Handle key-up events
	\code
	bool OnKeyUp(irr::EKEY_CODE keyCode)
	{
		if (keyCode == irr::KEY_ESCAPE)
		{
			if (Device)
			{
				scene::ICameraSceneNode * camera =
					Device->getSceneManager()->getActiveCamera();
				if (camera)
				{
					camera->setInputReceiverEnabled( !camera->isInputReceiverEnabled() );
				}
				return true;
			}
		}
		else if (keyCode == irr::KEY_F1)
		{
			if (Device)
			{
				IGUIElement* elem = Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId(GUI_ID_POSITION_TEXT);
				if (elem)
					elem->setVisible(!elem->isVisible());
			}
		}
		else if (keyCode == irr::KEY_KEY_M)
		{
			if (Device)
				Device->minimizeWindow();
		}
		else if (keyCode == irr::KEY_KEY_L)
		{
			UseLight=!UseLight;
			if (Model)
			{
				Model->setMaterialFlag(video::EMF_LIGHTING, UseLight);
				Model->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, UseLight);
			}
		}
		return false;
	}


	\endcode

		Handle "menu item clicked" events.
	\code
	void OnMenuItemSelected( IGUIContextMenu* menu )
	{
		s32 id = menu->getItemCommandId(menu->getSelectedItem());
		IGUIEnvironment* env = Device->getGUIEnvironment();

		switch(id)
		{
		case GUI_ID_OPEN_MODEL: // FilOnButtonSetScalinge -> Open Model
			env->addFileOpenDialog(L"Please select a model file to open");
			break;
		case GUI_ID_SET_MODEL_ARCHIVE: // File -> Set Model Archive
			env->addFileOpenDialog(L"Please select your game archive/directory");
			break;
		case GUI_ID_LOAD_AS_OCTREE: // File -> LoadAsOctree
			Octree = !Octree;
			menu->setItemChecked(menu->getSelectedItem(), Octree);
			break;
		case GUI_ID_QUIT: // File -> Quit
			Device->closeDevice();
			break;
		case GUI_ID_SKY_BOX_VISIBLE: // View -> Skybox
			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
			SkyBox->setVisible(!SkyBox->isVisible());
			break;
		case GUI_ID_DEBUG_OFF: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem()+1, false);
			menu->setItemChecked(menu->getSelectedItem()+2, false);
			menu->setItemChecked(menu->getSelectedItem()+3, false);
			menu->setItemChecked(menu->getSelectedItem()+4, false);
			menu->setItemChecked(menu->getSelectedItem()+5, false);
			menu->setItemChecked(menu->getSelectedItem()+6, false);
			if (Model)
				Model->setDebugDataVisible(scene::EDS_OFF);
			break;
		case GUI_ID_DEBUG_BOUNDING_BOX: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
			if (Model)
				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_BBOX));
			break;
		case GUI_ID_DEBUG_NORMALS: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
			if (Model)
				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_NORMALS));
			break;
		case GUI_ID_DEBUG_SKELETON: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
			if (Model)
				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_SKELETON));
			break;
		case GUI_ID_DEBUG_WIRE_OVERLAY: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
			if (Model)
				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_MESH_WIRE_OVERLAY));
			break;
		case GUI_ID_DEBUG_HALF_TRANSPARENT: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
			if (Model)
				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_HALF_TRANSPARENCY));
			break;
		case GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
			if (Model)
				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_BBOX_BUFFERS));
			break;
		case GUI_ID_DEBUG_ALL: // View -> Debug Information
			menu->setItemChecked(menu->getSelectedItem()-1, true);
			menu->setItemChecked(menu->getSelectedItem()-2, true);
			menu->setItemChecked(menu->getSelectedItem()-3, true);
			menu->setItemChecked(menu->getSelectedItem()-4, true);
			menu->setItemChecked(menu->getSelectedItem()-5, true);
			menu->setItemChecked(menu->getSelectedItem()-6, true);
			if (Model)
				Model->setDebugDataVisible(scene::EDS_FULL);
			break;
		case GUI_ID_ABOUT: // Help->About
			showAboutText();
			break;
		case GUI_ID_MODEL_MATERIAL_SOLID: // View -> Material -> Solid
			if (Model)
				Model->setMaterialType(video::EMT_SOLID);
			break;
		case GUI_ID_MODEL_MATERIAL_TRANSPARENT: // View -> Material -> Transparent
			if (Model)
				Model->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
			break;
		case GUI_ID_MODEL_MATERIAL_REFLECTION: // View -> Material -> Reflection
			if (Model)
				Model->setMaterialType(video::EMT_SPHERE_MAP);
			break;

		case GUI_ID_CAMERA_MAYA:
			setActiveCamera(Camera[0]);
			break;
		case GUI_ID_CAMERA_FIRST_PERSON:
			setActiveCamera(Camera[1]);
			break;
		}
	}

	\endcode

		Handle the event that one of the texture-filters was selected in the corresponding combobox.
	\code
	void OnTextureFilterSelected( IGUIComboBox* combo )
	{
		s32 pos = combo->getSelected();
		switch (pos)
		{
			case 0:
			if (Model)
			{
				Model->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
				Model->setMaterialFlag(video::EMF_TRILINEAR_FILTER, false);
				Model->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, false);
			}
			break;
			case 1:
			if (Model)
			{
				Model->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
				Model->setMaterialFlag(video::EMF_TRILINEAR_FILTER, false);
			}
			break;
			case 2:
			if (Model)
			{
				Model->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
				Model->setMaterialFlag(video::EMF_TRILINEAR_FILTER, true);
			}
			break;
			case 3:
			if (Model)
			{
				Model->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, true);
			}
			break;
			case 4:
			if (Model)
			{
				Model->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, false);
			}
			break;
		}
	}
};


\endcode

Most of the hard work is done. We only need to create the Irrlicht Engine
device and all the buttons, menus and toolbars. We start up the engine as
usual, using createDevice(). To make our application catch events, we set our
eventreceiver as parameter. As you can see, there is also a call to
IrrlichtDevice::setResizeable(). This makes the render window resizeable, which
is quite useful for a mesh viewer.
\code
int main(int argc, char* argv[])
{
	// ask user for driver

	video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D8;

	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 key;
	std::cin >> key;

	switch(key)
	{
		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;
	}

	// create device and exit if creation failed
	MyEventReceiver receiver;
	Device = createDevice(driverType, core::dimension2d<u32>(800, 600),
		16, false, false, false, &receiver);

	if (Device == 0)
		return 1; // could not create selected driver.

	Device->setResizable(true);

	Device->setWindowCaption(L"Irrlicht Engine - Loading...");

	video::IVideoDriver* driver = Device->getVideoDriver();
	IGUIEnvironment* env = Device->getGUIEnvironment();
	scene::ISceneManager* smgr = Device->getSceneManager();
	smgr->getParameters()->setAttribute(scene::COLLADA_CREATE_SCENE_INSTANCES, true);

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

	smgr->addLightSceneNode(0, core::vector3df(200,200,200),
		video::SColorf(1.0f,1.0f,1.0f),2000);
	smgr->setAmbientLight(video::SColorf(0.3f,0.3f,0.3f));
	// add our media directory as "search path"
	Device->getFileSystem()->addFolderFileArchive("../../media/");

	\endcode

	The next step is to read the configuration file. It is stored in the xml
	format and looks a little bit like this:

	@verbatim
	<?xml version="1.0"?>
	<config>
		<startUpModel file="some filename" />
		<messageText caption="Irrlicht Engine Mesh Viewer">
			Hello!
		</messageText>
	</config>
	@endverbatim

	We need the data stored in there to be written into the global variables
	StartUpModelFile, MessageText and Caption. This is now done using the
	Irrlicht Engine integrated XML parser:
	\code

	// read configuration from xml file

	io::IXMLReader* xml = Device->getFileSystem()->createXMLReader( L"config.xml");

	while(xml && xml->read())
	{
		switch(xml->getNodeType())
		{
		case io::EXN_TEXT:
			// in this xml file, the only text which occurs is the
			// messageText
			MessageText = xml->getNodeData();
			break;
		case io::EXN_ELEMENT:
			{
				if (core::stringw("startUpModel") == xml->getNodeName())
					StartUpModelFile = xml->getAttributeValue(L"file");
				else
				if (core::stringw("messageText") == xml->getNodeName())
					Caption = xml->getAttributeValue(L"caption");
			}
			break;
		default:
			break;
		}
	}

	if (xml)
		xml->drop(); // don't forget to delete the xml reader

	if (argc > 1)
		StartUpModelFile = argv[1];

	\endcode

	That wasn't difficult. Now we'll set a nicer font and create the Menu.
	It is possible to create submenus for every menu item. The call
	menu->addItem(L"File", -1, true, true); for example adds a new menu
	Item with the name "File" and the id -1. The following parameter says
	that the menu item should be enabled, and the last one says, that there
	should be a submenu. The submenu can now be accessed with
	menu->getSubMenu(0), because the "File" entry is the menu item with
	index 0.
	\code

	// set a nicer font

	IGUISkin* skin = env->getSkin();
	IGUIFont* font = env->getFont("fonthaettenschweiler.bmp");
	if (font)
		skin->setFont(font);

	// create menu
	gui::IGUIContextMenu* menu = env->addMenu();
	menu->addItem(L"File", -1, true, true);
	menu->addItem(L"View", -1, true, true);
	menu->addItem(L"Camera", -1, true, true);
	menu->addItem(L"Help", -1, true, true);

	gui::IGUIContextMenu* submenu;
	submenu = menu->getSubMenu(0);
	submenu->addItem(L"Open Model File & Texture...", GUI_ID_OPEN_MODEL);
	submenu->addItem(L"Set Model Archive...", GUI_ID_SET_MODEL_ARCHIVE);
	submenu->addItem(L"Load as Octree", GUI_ID_LOAD_AS_OCTREE);
	submenu->addSeparator();
	submenu->addItem(L"Quit", GUI_ID_QUIT);

	submenu = menu->getSubMenu(1);
	submenu->addItem(L"sky box visible", GUI_ID_SKY_BOX_VISIBLE, true, false, true);
	submenu->addItem(L"toggle model debug information", GUI_ID_TOGGLE_DEBUG_INFO, true, true);
	submenu->addItem(L"model material", -1, true, true );

	submenu = submenu->getSubMenu(1);
	submenu->addItem(L"Off", GUI_ID_DEBUG_OFF);
	submenu->addItem(L"Bounding Box", GUI_ID_DEBUG_BOUNDING_BOX);
	submenu->addItem(L"Normals", GUI_ID_DEBUG_NORMALS);
	submenu->addItem(L"Skeleton", GUI_ID_DEBUG_SKELETON);
	submenu->addItem(L"Wire overlay", GUI_ID_DEBUG_WIRE_OVERLAY);
	submenu->addItem(L"Half-Transparent", GUI_ID_DEBUG_HALF_TRANSPARENT);
	submenu->addItem(L"Buffers bounding boxes", GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES);
	submenu->addItem(L"All", GUI_ID_DEBUG_ALL);

	submenu = menu->getSubMenu(1)->getSubMenu(2);
	submenu->addItem(L"Solid", GUI_ID_MODEL_MATERIAL_SOLID);
	submenu->addItem(L"Transparent", GUI_ID_MODEL_MATERIAL_TRANSPARENT);
	submenu->addItem(L"Reflection", GUI_ID_MODEL_MATERIAL_REFLECTION);

	submenu = menu->getSubMenu(2);
	submenu->addItem(L"Maya Style", GUI_ID_CAMERA_MAYA);
	submenu->addItem(L"First Person", GUI_ID_CAMERA_FIRST_PERSON);

	submenu = menu->getSubMenu(3);
	submenu->addItem(L"About", GUI_ID_ABOUT);

	\endcode

	Below the menu we want a toolbar, onto which we can place colored
	buttons and important looking stuff like a senseless combobox.
	\code

	// create toolbar

	gui::IGUIToolBar* bar = env->addToolBar();

	video::ITexture* image = driver->getTexture("open.png");
	bar->addButton(GUI_ID_BUTTON_OPEN_MODEL, 0, L"Open a model",image, 0, false, true);

	image = driver->getTexture("tools.png");
	bar->addButton(GUI_ID_BUTTON_SHOW_TOOLBOX, 0, L"Open Toolset",image, 0, false, true);

	image = driver->getTexture("zip.png");
	bar->addButton(GUI_ID_BUTTON_SELECT_ARCHIVE, 0, L"Set Model Archive",image, 0, false, true);

	image = driver->getTexture("help.png");
	bar->addButton(GUI_ID_BUTTON_SHOW_ABOUT, 0, L"Open Help", image, 0, false, true);

	// create a combobox with some senseless texts

	gui::IGUIComboBox* box = env->addComboBox(core::rect<s32>(250,4,350,23), bar, GUI_ID_TEXTUREFILTER);
	box->addItem(L"No filtering");
	box->addItem(L"Bilinear");
	box->addItem(L"Trilinear");
	box->addItem(L"Anisotropic");
	box->addItem(L"Isotropic");

	\endcode

	To make the editor look a little bit better, we disable transparent gui
	elements, and add an Irrlicht Engine logo. In addition, a text showing
	the current frames per second value is created and the window caption is
	changed.
	\code

	// disable alpha

	for (s32 i=0; i<gui::EGDC_COUNT ; ++i)
	{
		video::SColor col = env->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
		col.setAlpha(255);
		env->getSkin()->setColor((gui::EGUI_DEFAULT_COLOR)i, col);
	}

	// add a tabcontrol

	createToolBox();

	// create fps text

	IGUIStaticText* fpstext = env->addStaticText(L"",
			core::rect<s32>(400,4,570,23), true, false, bar);

	IGUIStaticText* postext = env->addStaticText(L"",
			core::rect<s32>(10,50,470,80),false, false, 0, GUI_ID_POSITION_TEXT);
	postext->setVisible(false);

	// set window caption

	Caption += " - [";
	Caption += driver->getName();
	Caption += "]";
	Device->setWindowCaption(Caption.c_str());

	\endcode

	That's nearly the whole application. We simply show the about message
	box at start up, and load the first model. To make everything look
	better, a skybox is created and a user controled camera, to make the
	application a little bit more interactive. Finally, everything is drawn
	in a standard drawing loop.
	\code

	// show about message box and load default model
	if (argc==1)
		showAboutText();
	loadModel(StartUpModelFile.c_str());

	// add skybox

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

	// add a camera scene node
	Camera[0] = smgr->addCameraSceneNodeMaya();
	Camera[0]->setFarValue(20000.f);
	// Maya cameras reposition themselves relative to their target, so target the location
	// where the mesh scene node is placed.
	Camera[0]->setTarget(core::vector3df(0,30,0));

	Camera[1] = smgr->addCameraSceneNodeFPS();
	Camera[1]->setFarValue(20000.f);
	Camera[1]->setPosition(core::vector3df(0,0,-70));
	Camera[1]->setTarget(core::vector3df(0,30,0));

	setActiveCamera(Camera[0]);

	// load the irrlicht engine logo
	IGUIImage *img =
		env->addImage(driver->getTexture("irrlichtlogo2.png"),
			core::position2d<s32>(10, driver->getScreenSize().Height - 128));

	// lock the logo's edges to the bottom left corner of the screen
	img->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT,
			EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);

	// draw everything

	while(Device->run() && driver)
	{
		if (Device->isWindowActive())
		{
			driver->beginScene(true, true, video::SColor(150,50,50,50));

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

			driver->endScene();

			core::stringw str(L"FPS: ");
			str.append(core::stringw(driver->getFPS()));
			str += L" Tris: ";
			str.append(core::stringw(driver->getPrimitiveCountDrawn()));
			fpstext->setText(str.c_str());

			scene::ICameraSceneNode* cam = Device->getSceneManager()->getActiveCamera();
			str = L"Pos: ";
			str.append(core::stringw(cam->getPosition().X));
			str += L" ";
			str.append(core::stringw(cam->getPosition().Y));
			str += L" ";
			str.append(core::stringw(cam->getPosition().Z));
			str += L" Tgt: ";
			str.append(core::stringw(cam->getTarget().X));
			str += L" ";
			str.append(core::stringw(cam->getTarget().Y));
			str += L" ";
			str.append(core::stringw(cam->getTarget().Z));
			postext->setText(str.c_str());
		}
		else
			Device->yield();
	}

	Device->drop();
	return 0;
}

\endcode

**/
/** \page example010 Tutorial 10: Shaders
 \image html "010shot.jpg"
 \image latex "010shot.jpg"

This tutorial shows how to use shaders for D3D8, D3D9, and OpenGL with the
engine and how to create new material types with them. It also shows how to
disable the generation of mipmaps at texture loading, and how to use text scene
nodes.

This tutorial does not explain how shaders work. I would recommend to read the
D3D or OpenGL documentation, to search a tutorial, or to read a book about
this.

At first, we need to include all headers and do the stuff we always do, like in
nearly all other tutorials:
\code
#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

\endcode

Because we want to use some interesting shaders in this tutorials, we need to
set some data for them to make them able to compute nice colors. In this
example, we'll use a simple vertex shader which will calculate the color of the
vertex based on the position of the camera.
For this, the shader needs the following data: The inverted world matrix for
transforming the normal, the clip matrix for transforming the position, the
camera position and the world position of the object for the calculation of the
angle of light, and the color of the light. To be able to tell the shader all
this data every frame, we have to derive a class from the
IShaderConstantSetCallBack interface and override its only method, namely
OnSetConstants(). This method will be called every time the material is set.
The method setVertexShaderConstant() of the IMaterialRendererServices interface
is used to set the data the shader needs. If the user chose to use a High Level
shader language like HLSL instead of Assembler in this example, you have to set
the variable name as parameter instead of the register index.
\code

IrrlichtDevice* device = 0;
bool UseHighLevelShaders = false;

class MyShaderCallBack : public video::IShaderConstantSetCallBack
{
public:

	virtual void OnSetConstants(video::IMaterialRendererServices* services,
			s32 userData)
	{
		video::IVideoDriver* driver = services->getVideoDriver();

		// set inverted world matrix
		// if we are using highlevel shaders (the user can select this when
		// starting the program), we must set the constants by name.

		core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
		invWorld.makeInverse();

		if (UseHighLevelShaders)
			services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
		else
			services->setVertexShaderConstant(invWorld.pointer(), 0, 4);

		// set clip matrix

		core::matrix4 worldViewProj;
		worldViewProj = driver->getTransform(video::ETS_PROJECTION);
		worldViewProj *= driver->getTransform(video::ETS_VIEW);
		worldViewProj *= driver->getTransform(video::ETS_WORLD);

		if (UseHighLevelShaders)
			services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
		else
			services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);

		// set camera position

		core::vector3df pos = device->getSceneManager()->
			getActiveCamera()->getAbsolutePosition();

		if (UseHighLevelShaders)
			services->setVertexShaderConstant("mLightPos", reinterpret_cast<f32*>(&pos), 3);
		else
			services->setVertexShaderConstant(reinterpret_cast<f32*>(&pos), 8, 1);

		// set light color

		video::SColorf col(0.0f,1.0f,1.0f,0.0f);

		if (UseHighLevelShaders)
			services->setVertexShaderConstant("mLightColor",
					reinterpret_cast<f32*>(&col), 4);
		else
			services->setVertexShaderConstant(reinterpret_cast<f32*>(&col), 9, 1);

		// set transposed world matrix

		core::matrix4 world = driver->getTransform(video::ETS_WORLD);
		world = world.getTransposed();

		if (UseHighLevelShaders)
			services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
		else
			services->setVertexShaderConstant(world.pointer(), 10, 4);
	}
};

\endcode

The next few lines start up the engine just like in most other tutorials
before. But in addition, we ask the user if he wants to use high level shaders
in this example, if he selected a driver which is capable of doing so.
\code
int main()
{
	// let user select driver type

	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;
	}

	// ask the user if we should use high level shaders for this example
	if (driverType == video::EDT_DIRECT3D9 ||
		 driverType == video::EDT_OPENGL)
	{
		printf("Please press 'y' if you want to use high level shaders.\n");
		std::cin >> i;
		if (i == 'y')
			UseHighLevelShaders = true;
	}

	// create device

	device = createDevice(driverType, core::dimension2d<u32>(640, 480));

	if (device == 0)
		return 1; // could not create selected driver.


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

	\endcode

	Now for the more interesting parts. If we are using Direct3D, we want
	to load vertex and pixel shader programs, if we have OpenGL, we want to
	use ARB fragment and vertex programs. I wrote the corresponding
	programs down into the files d3d8.ps, d3d8.vs, d3d9.ps, d3d9.vs,
	opengl.ps and opengl.vs. We only need the right filenames now. This is
	done in the following switch. Note, that it is not necessary to write
	the shaders into text files, like in this example. You can even write
	the shaders directly as strings into the cpp source file, and use later
	addShaderMaterial() instead of addShaderMaterialFromFiles().
	\code

	io::path vsFileName; // filename for the vertex shader
	io::path psFileName; // filename for the pixel shader

	switch(driverType)
	{
	case video::EDT_DIRECT3D8:
		psFileName = "../../media/d3d8.psh";
		vsFileName = "../../media/d3d8.vsh";
		break;
	case video::EDT_DIRECT3D9:
		if (UseHighLevelShaders)
		{
			psFileName = "../../media/d3d9.hlsl";
			vsFileName = psFileName; // both shaders are in the same file
		}
		else
		{
			psFileName = "../../media/d3d9.psh";
			vsFileName = "../../media/d3d9.vsh";
		}
		break;

	case video::EDT_OPENGL:
		if (UseHighLevelShaders)
		{
			psFileName = "../../media/opengl.frag";
			vsFileName = "../../media/opengl.vert";
		}
		else
		{
			psFileName = "../../media/opengl.psh";
			vsFileName = "../../media/opengl.vsh";
		}
		break;
	}

	\endcode

	In addition, we check if the hardware and the selected renderer is
	capable of executing the shaders we want. If not, we simply set the
	filename string to 0. This is not necessary, but useful in this
	example: For example, if the hardware is able to execute vertex shaders
	but not pixel shaders, we create a new material which only uses the
	vertex shader, and no pixel shader. Otherwise, if we would tell the
	engine to create this material and the engine sees that the hardware
	wouldn't be able to fullfill the request completely, it would not
	create any new material at all. So in this example you would see at
	least the vertex shader in action, without the pixel shader.
	\code

	if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
		!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))
	{
		device->getLogger()->log("WARNING: Pixel shaders disabled "\
			"because of missing driver/hardware support.");
		psFileName = "";
	}

	if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
		!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
	{
		device->getLogger()->log("WARNING: Vertex shaders disabled "\
			"because of missing driver/hardware support.");
		vsFileName = "";
	}

	\endcode

	Now lets create the new materials. As you maybe know from previous
	examples, a material type in the Irrlicht engine is set by simply
	changing the MaterialType value in the SMaterial struct. And this value
	is just a simple 32 bit value, like video::EMT_SOLID. So we only need
	the engine to create a new value for us which we can set there. To do
	this, we get a pointer to the IGPUProgrammingServices and call
	addShaderMaterialFromFiles(), which returns such a new 32 bit value.
	That's all.

	The parameters to this method are the following: First, the names of
	the files containing the code of the vertex and the pixel shader. If
	you would use addShaderMaterial() instead, you would not need file
	names, then you could write the code of the shader directly as string.
	The following parameter is a pointer to the IShaderConstantSetCallBack
	class we wrote at the beginning of this tutorial. If you don't want to
	set constants, set this to 0. The last paramter tells the engine which
	material it should use as base material.

	To demonstrate this, we create two materials with a different base
	material, one with EMT_SOLID and one with EMT_TRANSPARENT_ADD_COLOR.
	\code

	// create materials

	video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
	s32 newMaterialType1 = 0;
	s32 newMaterialType2 = 0;

	if (gpu)
	{
		MyShaderCallBack* mc = new MyShaderCallBack();

		// create the shaders depending on if the user wanted high level
		// or low level shaders:

		if (UseHighLevelShaders)
		{
			// create material from high level shaders (hlsl or glsl)

			newMaterialType1 = gpu->addHighLevelShaderMaterialFromFiles(
				vsFileName, "vertexMain", video::EVST_VS_1_1,
				psFileName, "pixelMain", video::EPST_PS_1_1,
				mc, video::EMT_SOLID);

			newMaterialType2 = gpu->addHighLevelShaderMaterialFromFiles(
				vsFileName, "vertexMain", video::EVST_VS_1_1,
				psFileName, "pixelMain", video::EPST_PS_1_1,
				mc, video::EMT_TRANSPARENT_ADD_COLOR);
		}
		else
		{
			// create material from low level shaders (asm or arb_asm)

			newMaterialType1 = gpu->addShaderMaterialFromFiles(vsFileName,
				psFileName, mc, video::EMT_SOLID);

			newMaterialType2 = gpu->addShaderMaterialFromFiles(vsFileName,
				psFileName, mc, video::EMT_TRANSPARENT_ADD_COLOR);
		}

		mc->drop();
	}

	\endcode

	Now it's time for testing the materials. We create a test cube and set
	the material we created. In addition, we add a text scene node to the
	cube and a rotation animator to make it look more interesting and
	important.
	\code

	// create test scene node 1, with the new created material type 1

	scene::ISceneNode* node = smgr->addCubeSceneNode(50);
	node->setPosition(core::vector3df(0,0,0));
	node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
	node->setMaterialFlag(video::EMF_LIGHTING, false);
	node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1);

	smgr->addTextSceneNode(gui->getBuiltInFont(),
			L"PS & VS & EMT_SOLID",
			video::SColor(255,255,255,255),	node);

	scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(
			core::vector3df(0,0.3f,0));
	node->addAnimator(anim);
	anim->drop();

	\endcode

	Same for the second cube, but with the second material we created.
	\code

	// create test scene node 2, with the new created material type 2

	node = smgr->addCubeSceneNode(50);
	node->setPosition(core::vector3df(0,-10,50));
	node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
	node->setMaterialFlag(video::EMF_LIGHTING, false);
	node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType2);

	smgr->addTextSceneNode(gui->getBuiltInFont(),
			L"PS & VS & EMT_TRANSPARENT",
			video::SColor(255,255,255,255),	node);

	anim = smgr->createRotationAnimator(core::vector3df(0,0.3f,0));
	node->addAnimator(anim);
	anim->drop();

	\endcode

	Then we add a third cube without a shader on it, to be able to compare
	the cubes.
	\code

	// add a scene node with no shader

	node = smgr->addCubeSceneNode(50);
	node->setPosition(core::vector3df(0,50,25));
	node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
	node->setMaterialFlag(video::EMF_LIGHTING, false);
	smgr->addTextSceneNode(gui->getBuiltInFont(), L"NO SHADER",
		video::SColor(255,255,255,255), node);

	\endcode

	And last, we add a skybox and a user controlled camera to the scene.
	For the skybox textures, we disable mipmap generation, because we don't
	need mipmaps on it.
	\code

	// add a nice skybox

	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);

	// add a camera and disable the mouse cursor

	scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS();
	cam->setPosition(core::vector3df(-100,50,100));
	cam->setTarget(core::vector3df(0,0,0));
	device->getCursorControl()->setVisible(false);

	\endcode

	Now draw everything. That's all.
	\code

	int lastFPS = -1;

	while(device->run())
		if (device->isWindowActive())
	{
		driver->beginScene(true, true, video::SColor(255,0,0,0));
		smgr->drawAll();
		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps)
		{
			core::stringw str = L"Irrlicht Engine - Vertex and pixel shader example [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;

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

	device->drop();

	return 0;
}

\endcode

Compile and run this, and I hope you have fun with your new little shader
writing tool :).
**/
/** \page example011 Tutorial 11: Per-Pixel Lighting
 \image html "011shot.jpg"
 \image latex "011shot.jpg"

This tutorial shows how to use one of the built in more complex materials in
irrlicht: Per pixel lighted surfaces using normal maps and parallax mapping. It
will also show how to use fog and moving particle systems. And don't panic: You
dont need any experience with shaders to use these materials in Irrlicht.

At first, we need to include all headers and do the stuff we always do, like in
nearly all other tutorials.
\code
#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

\endcode

For this example, we need an event receiver, to make it possible for the user
to switch between the three available material types. In addition, the event
receiver will create some small GUI window which displays what material is
currently being used. There is nothing special done in this class, so maybe you
want to skip reading it.
\code
class MyEventReceiver : public IEventReceiver
{
public:

	MyEventReceiver(scene::ISceneNode* room,
		gui::IGUIEnvironment* env, video::IVideoDriver* driver)
	{
		// store pointer to room so we can change its drawing mode
		Room = room;
		Driver = driver;

		// set a nicer font
		gui::IGUISkin* skin = env->getSkin();
		gui::IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
		if (font)
			skin->setFont(font);

		// add window and listbox
		gui::IGUIWindow* window = env->addWindow(
			core::rect<s32>(460,375,630,470), false, L"Use 'E' + 'R' to change");

		ListBox = env->addListBox(
			core::rect<s32>(2,22,165,88), window);

		ListBox->addItem(L"Diffuse");
		ListBox->addItem(L"Bump mapping");
		ListBox->addItem(L"Parallax mapping");
		ListBox->setSelected(1);

		// create problem text
		ProblemText = env->addStaticText(
			L"Your hardware or this renderer is not able to use the "\
			L"needed shaders for this material. Using fall back materials.",
			core::rect<s32>(150,20,470,80));

		ProblemText->setOverrideColor(video::SColor(100,255,255,255));

		// set start material (prefer parallax mapping if available)
		video::IMaterialRenderer* renderer =
			Driver->getMaterialRenderer(video::EMT_PARALLAX_MAP_SOLID);
		if (renderer && renderer->getRenderCapability() == 0)
			ListBox->setSelected(2);

		// set the material which is selected in the listbox
		setMaterial();
	}

	bool OnEvent(const SEvent& event)
	{
		// check if user presses the key 'E' or 'R'
		if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
			!event.KeyInput.PressedDown && Room && ListBox)
		{
			// change selected item in listbox

			int sel = ListBox->getSelected();
			if (event.KeyInput.Key == irr::KEY_KEY_R)
				++sel;
			else
			if (event.KeyInput.Key == irr::KEY_KEY_E)
				--sel;
			else
				return false;

			if (sel > 2) sel = 0;
			if (sel < 0) sel = 2;
			ListBox->setSelected(sel);

			// set the material which is selected in the listbox
			setMaterial();
		}

		return false;
	}

private:

	// sets the material of the room mesh the the one set in the
	// list box.
	void setMaterial()
	{
		video::E_MATERIAL_TYPE type = video::EMT_SOLID;

		// change material setting
		switch(ListBox->getSelected())
		{
		case 0: type = video::EMT_SOLID;
			break;
		case 1: type = video::EMT_NORMAL_MAP_SOLID;
			break;
		case 2: type = video::EMT_PARALLAX_MAP_SOLID;
			break;
		}

		Room->setMaterialType(type);

		\endcode

		We need to add a warning if the materials will not be able to
		be displayed 100% correctly. This is no problem, they will be
		renderered using fall back materials, but at least the user
		should know that it would look better on better hardware. We
		simply check if the material renderer is able to draw at full
		quality on the current hardware. The
		IMaterialRenderer::getRenderCapability() returns 0 if this is
		the case.
		\code
		video::IMaterialRenderer* renderer = Driver->getMaterialRenderer(type);

		// display some problem text when problem
		if (!renderer || renderer->getRenderCapability() != 0)
			ProblemText->setVisible(true);
		else
			ProblemText->setVisible(false);
	}

private:

	gui::IGUIStaticText* ProblemText;
	gui::IGUIListBox* ListBox;

	scene::ISceneNode* Room;
	video::IVideoDriver* Driver;
};


\endcode

Now for the real fun. We create an Irrlicht Device and start to setup the scene.
\code
int main()
{
	// let user select driver type

	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 0;
	}

	// create device

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

	if (device == 0)
		return 1; // could not create selected driver.


	\endcode

	Before we start with the interesting stuff, we do some simple things:
	Store pointers to the most important parts of the engine (video driver,
	scene manager, gui environment) to safe us from typing too much, add an
	irrlicht engine logo to the window and a user controlled first person
	shooter style camera. Also, we let the engine know that it should store
	all textures in 32 bit. This necessary because for parallax mapping, we
	need 32 bit textures.
	\code

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

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

	// add irrlicht logo
	env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
		core::position2d<s32>(10,10));

	// add camera
	scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
	camera->setPosition(core::vector3df(-200,200,-200));

	// disable mouse cursor
	device->getCursorControl()->setVisible(false);


	\endcode

	Because we want the whole scene to look a little bit scarier, we add
	some fog to it. This is done by a call to IVideoDriver::setFog(). There
	you can set various fog settings. In this example, we use pixel fog,
	because it will work well with the materials we'll use in this example.
	Please note that you will have to set the material flag EMF_FOG_ENABLE
	to 'true' in every scene node which should be affected by this fog.
	\code
	driver->setFog(video::SColor(0,138,125,81), video::EFT_FOG_LINEAR, 250, 1000, .003f, true, false);

	\endcode

	To be able to display something interesting, we load a mesh from a .3ds
	file which is a room I modeled with anim8or. It is the same room as
	from the specialFX example. Maybe you remember from that tutorial, I am
	no good modeler at all and so I totally messed up the texture mapping
	in this model, but we can simply repair it with the
	IMeshManipulator::makePlanarTextureMapping() method.
	\code

	scene::IAnimatedMesh* roomMesh = smgr->getMesh(
		"../../media/room.3ds");
	scene::ISceneNode* room = 0;

	if (roomMesh)
	{
		smgr->getMeshManipulator()->makePlanarTextureMapping(
				roomMesh->getMesh(0), 0.003f);

		\endcode

		Now for the first exciting thing: If we successfully loaded the
		mesh we need to apply textures to it. Because we want this room
		to be displayed with a very cool material, we have to do a
		little bit more than just set the textures. Instead of only
		loading a color map as usual, we also load a height map which
		is simply a grayscale texture. From this height map, we create
		a normal map which we will set as second texture of the room.
		If you already have a normal map, you could directly set it,
		but I simply didn't find a nice normal map for this texture.
		The normal map texture is being generated by the
		makeNormalMapTexture method of the VideoDriver. The second
		parameter specifies the height of the heightmap. If you set it
		to a bigger value, the map will look more rocky.
		\code

		video::ITexture* normalMap =
			driver->getTexture("../../media/rockwall_height.bmp");

		if (normalMap)
			driver->makeNormalMapTexture(normalMap, 9.0f);

		\endcode

		But just setting color and normal map is not everything. The
		material we want to use needs some additional informations per
		vertex like tangents and binormals. Because we are too lazy to
		calculate that information now, we let Irrlicht do this for us.
		That's why we call IMeshManipulator::createMeshWithTangents().
		It creates a mesh copy with tangents and binormals from another
		mesh. After we've done that, we simply create a standard
		mesh scene node with this mesh copy, set color and normal map
		and adjust some other material settings. Note that we set
		EMF_FOG_ENABLE to true to enable fog in the room.
		\code

		scene::IMesh* tangentMesh = smgr->getMeshManipulator()->createMeshWithTangents(
			roomMesh->getMesh(0));

		room = smgr->addMeshSceneNode(tangentMesh);
		room->setMaterialTexture(0,
				driver->getTexture("../../media/rockwall.jpg"));
		room->setMaterialTexture(1, normalMap);

		room->getMaterial(0).SpecularColor.set(0,0,0,0);

		room->setMaterialFlag(video::EMF_FOG_ENABLE, true);
		room->setMaterialType(video::EMT_PARALLAX_MAP_SOLID);
		// adjust height for parallax effect
		room->getMaterial(0).MaterialTypeParam = 0.035f;

		// drop mesh because we created it with a create.. call.
		tangentMesh->drop();
	}

	\endcode

	After we've created a room shaded by per pixel lighting, we add a
	sphere into it with the same material, but we'll make it transparent.
	In addition, because the sphere looks somehow like a familiar planet,
	we make it rotate. The procedure is similar as before. The difference
	is that we are loading the mesh from an .x file which already contains
	a color map so we do not need to load it manually. But the sphere is a
	little bit too small for our needs, so we scale it by the factor 50.
	\code

	// add earth sphere

	scene::IAnimatedMesh* earthMesh = smgr->getMesh("../../media/earth.x");
	if (earthMesh)
	{
		//perform various task with the mesh manipulator
		scene::IMeshManipulator *manipulator = smgr->getMeshManipulator();

		// create mesh copy with tangent informations from original earth.x mesh
		scene::IMesh* tangentSphereMesh =
			manipulator->createMeshWithTangents(earthMesh->getMesh(0));

		// set the alpha value of all vertices to 200
		manipulator->setVertexColorAlpha(tangentSphereMesh, 200);

		// scale the mesh by factor 50
		core::matrix4 m;
		m.setScale ( core::vector3df(50,50,50) );
		manipulator->transformMesh( tangentSphereMesh, m );

		scene::ISceneNode *sphere = smgr->addMeshSceneNode(tangentSphereMesh);

		sphere->setPosition(core::vector3df(-70,130,45));

		// load heightmap, create normal map from it and set it
		video::ITexture* earthNormalMap = driver->getTexture("../../media/earthbump.jpg");
		if (earthNormalMap)
		{
			driver->makeNormalMapTexture(earthNormalMap, 20.0f);
			sphere->setMaterialTexture(1, earthNormalMap);
			sphere->setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
		}

		// adjust material settings
		sphere->setMaterialFlag(video::EMF_FOG_ENABLE, true);

		// add rotation animator
		scene::ISceneNodeAnimator* anim =
			smgr->createRotationAnimator(core::vector3df(0,0.1f,0));
		sphere->addAnimator(anim);
		anim->drop();

		// drop mesh because we created it with a create.. call.
		tangentSphereMesh->drop();
	}

	\endcode

	Per pixel lighted materials only look cool when there are moving
	lights. So we add some. And because moving lights alone are so boring,
	we add billboards to them, and a whole particle system to one of them.
	We start with the first light which is red and has only the billboard
	attached.
	\code

	// add light 1 (nearly red)
	scene::ILightSceneNode* light1 =
		smgr->addLightSceneNode(0, core::vector3df(0,0,0),
		video::SColorf(0.5f, 1.0f, 0.5f, 0.0f), 800.0f);

	light1->setDebugDataVisible ( scene::EDS_BBOX );


	// add fly circle animator to light 1
	scene::ISceneNodeAnimator* anim =
		smgr->createFlyCircleAnimator (core::vector3df(50,300,0),190.0f, -0.003f);
	light1->addAnimator(anim);
	anim->drop();

	// attach billboard to the light
	scene::ISceneNode* bill =
		smgr->addBillboardSceneNode(light1, core::dimension2d<f32>(60, 60));

	bill->setMaterialFlag(video::EMF_LIGHTING, false);
	bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
	bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
	bill->setMaterialTexture(0, driver->getTexture("../../media/particlered.bmp"));

	\endcode

	Now the same again, with the second light. The difference is that we
	add a particle system to it too. And because the light moves, the
	particles of the particlesystem will follow. If you want to know more
	about how particle systems are created in Irrlicht, take a look at the
	specialFx example. Maybe you will have noticed that we only add 2
	lights, this has a simple reason: The low end version of this material
	was written in ps1.1 and vs1.1, which doesn't allow more lights. You
	could add a third light to the scene, but it won't be used to shade the
	walls. But of course, this will change in future versions of Irrlicht
	where higher versions of pixel/vertex shaders will be implemented too.
	\code

	// add light 2 (gray)
	scene::ISceneNode* light2 =
		smgr->addLightSceneNode(0, core::vector3df(0,0,0),
		video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 800.0f);

	// add fly circle animator to light 2
	anim = smgr->createFlyCircleAnimator(core::vector3df(0,150,0), 200.0f,
			0.001f, core::vector3df(0.2f, 0.9f, 0.f));
	light2->addAnimator(anim);
	anim->drop();

	// attach billboard to light
	bill = smgr->addBillboardSceneNode(light2, core::dimension2d<f32>(120, 120));
	bill->setMaterialFlag(video::EMF_LIGHTING, false);
	bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
	bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
	bill->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));

	// add particle system
	scene::IParticleSystemSceneNode* ps =
		smgr->addParticleSystemSceneNode(false, light2);

	// create and set emitter
	scene::IParticleEmitter* em = ps->createBoxEmitter(
		core::aabbox3d<f32>(-3,0,-3,3,1,3),
		core::vector3df(0.0f,0.03f,0.0f),
		80,100,
		video::SColor(0,255,255,255), video::SColor(0,255,255,255),
		400,1100);
	em->setMinStartSize(core::dimension2d<f32>(30.0f, 40.0f));
	em->setMaxStartSize(core::dimension2d<f32>(30.0f, 40.0f));

	ps->setEmitter(em);
	em->drop();

	// create and set affector
	scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
	ps->addAffector(paf);
	paf->drop();

	// adjust some material settings
	ps->setMaterialFlag(video::EMF_LIGHTING, false);
	ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
	ps->setMaterialTexture(0, driver->getTexture("../../media/fireball.bmp"));
	ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);


	MyEventReceiver receiver(room, env, driver);
	device->setEventReceiver(&receiver);

	\endcode

	Finally, draw everything. That's it.
	\code

	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"Per pixel lighting example - Irrlicht Engine [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;

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

	device->drop();

	return 0;
}

\endcode

**/
/** \page example012 Tutorial 12: Terrain Rendering
 \image html "012shot.jpg"
 \image latex "012shot.jpg"

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.
\code
#include <irrlicht.h>
#include <iostream>

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)
	{
		Skybox->setVisible(true);
		Skydome->setVisible(false);
	}

	bool OnEvent(const SEvent& event)
	{
		// check if user presses the key 'W' or 'D'
		if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
		{
			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;
			default:
				break;
			}
		}

		return false;
	}

private:
	scene::ISceneNode* Terrain;
	scene::ISceneNode* Skybox;
	scene::ISceneNode* Skydome;
	bool showBox;
};


\endcode

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.
\code
int main()
{
	// let user select driver type

	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;
	}	

	// create device with full flexibility over creation parameters
	// you can add more parameters if desired, check irr::SIrrlichtCreationParameters
	irr::SIrrlichtCreationParameters params;
	params.DriverType=driverType;
	params.WindowSize=core::dimension2d<u32>(640, 480);
	IrrlichtDevice* device = createDeviceEx(params);

	if (device == 0)
		return 1; // could not create selected driver.

	
	\endcode

	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.
	\code

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

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

	// add irrlicht logo
	env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
		core::position2d<s32>(10,10));

	//set other font
	env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));

	// add some help text
	env->addStaticText(
		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
	scene::ICameraSceneNode* 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);

	\endcode

	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.
	\code

	// add terrain scene node
	scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
		"../../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);
	//terrain->setDebugDataVisible ( true );

	\endcode

	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.
	\code

	// create triangle selector for the terrain	
	scene::ITriangleSelector* selector
		= smgr->createTerrainTriangleSelector(terrain, 0);
	terrain->setTriangleSelector(selector);

	// create collision response animator and attach it to the camera
	scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
		selector, camera, core::vector3df(60,100,60),
		core::vector3df(0,0,0),
		core::vector3df(0,50,0));
	selector->drop();
	camera->addAnimator(anim);
	anim->drop();

	\endcode  If you need access to the terrain data you can also do this directly via the following code fragment.
	\code
	scene::CDynamicMeshBuffer* buffer = new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
	terrain->getMeshBufferForLOD(*buffer, 0);
	video::S3DVertex2TCoords* data = (video::S3DVertex2TCoords*)buffer->getVertexBuffer().getData();
	// Work on data or get the IndexBuffer with a similar call.
	buffer->drop(); // When done drop the buffer again.

	\endcode

	To make the user be able to switch between normal and wireframe mode,
	we create an instance of the event reciever 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'.
	\code

	// create skybox and skydome
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

	scene::ISceneNode* skybox=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"));
	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);

	\endcode

	That's it, draw everything.
	\code

	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;
}

\endcode

Now you know how to use terrain in Irrlicht.
**/
/** \page example013 Tutorial 13: Render To Texture
 \image html "013shot.jpg"
 \image latex "013shot.jpg"

This tutorial shows how to render to a texture using Irrlicht. Render to
texture is a feature with which it is possible to create nice special effects.
In addition, this tutorial shows how to enable specular highlights.

In the beginning, everything as usual. Include the needed headers, ask the user
for the rendering driver, create the Irrlicht Device:
\code

#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

int main()
{
	// let user select driver type

	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;
	}	

	// create device and exit if creation failed

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

	if (device == 0)
		return 1; // could not create selected driver.

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

	Now, we load an animated mesh to be displayed. As in most examples,
	we'll take the fairy md2 model. The difference here: We set the
	shininess of the model to a value other than 0 which is the default
	value. This enables specular highlights on the model if dynamic
	lighting is on. The value influences the size of the highlights.
	\code

	// load and display animated fairy mesh

	scene::IAnimatedMeshSceneNode* fairy = smgr->addAnimatedMeshSceneNode(
		smgr->getMesh("../../media/faerie.md2"));

	if (fairy)
	{
		fairy->setMaterialTexture(0,
				driver->getTexture("../../media/faerie2.bmp")); // set diffuse texture
		fairy->setMaterialFlag(video::EMF_LIGHTING, true); // enable dynamic lighting
		fairy->getMaterial(0).Shininess = 20.0f; // set size of specular highlights
		fairy->setPosition(core::vector3df(-10,0,-100));
		fairy->setMD2Animation ( scene::EMAT_STAND );
	}
	
	\endcode

	To make specular highlights appear on the model, we need a dynamic
	light in the scene. We add one directly in vicinity of the model. In
	addition, to make the model not that dark, we set the ambient light to
	gray.
	\code

	// add white light
	smgr->addLightSceneNode(0, core::vector3df(-15,5,-105),
			video::SColorf(1.0f, 1.0f, 1.0f));

	// set ambient light
	smgr->setAmbientLight(video::SColor(0,60,60,60));
	
	\endcode

	The next is just some standard stuff: Add a user controlled camera to
	the scene, disable mouse cursor, and add a test cube and let it rotate
	to make the scene more interesting.
	\code

	// add fps camera
	scene::ICameraSceneNode* fpsCamera = smgr->addCameraSceneNodeFPS();
	fpsCamera->setPosition(core::vector3df(-50,50,-150));

	// disable mouse cursor
	device->getCursorControl()->setVisible(false);

	// create test cube
	scene::ISceneNode* test = smgr->addCubeSceneNode(60);

	// let the cube rotate and set some light settings
	scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(
		core::vector3df(0.3f, 0.3f,0));

	test->setPosition(core::vector3df(-100,0,-100));
	test->setMaterialFlag(video::EMF_LIGHTING, false); // disable dynamic lighting
	test->addAnimator(anim);
	anim->drop();

	// set window caption
	device->setWindowCaption(L"Irrlicht Engine - Render to Texture and Specular Highlights example");
	
	\endcode

	To test out the render to texture feature, we need a render target
	texture. These are not like standard textures, but need to be created
	first. To create one, we call IVideoDriver::addRenderTargetTexture()
	and specify the size of the texture. Please don't use sizes bigger than
	the frame buffer for this, because the render target shares the zbuffer
	with the frame buffer.
	Because we want to render the scene not from the user camera into the
	texture, we add another fixed camera to the scene. But before we do all
	this, we check if the current running driver is able to render to
	textures. If it is not, we simply display a warning text.
	\code

	// create render target
	video::ITexture* rt = 0;
	scene::ICameraSceneNode* fixedCam = 0;
	

	if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET))
	{
		rt = driver->addRenderTargetTexture(core::dimension2d<u32>(256,256), "RTT1");
		test->setMaterialTexture(0, rt); // set material of cube to render target

		// add fixed camera
		fixedCam = smgr->addCameraSceneNode(0, core::vector3df(10,10,-80),
			core::vector3df(-10,10,-100));
	}
	else
	{
		// create problem text
		gui::IGUISkin* skin = env->getSkin();
		gui::IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
		if (font)
			skin->setFont(font);

		gui::IGUIStaticText* text = env->addStaticText(
			L"Your hardware or this renderer is not able to use the "\
			L"render to texture feature. RTT Disabled.",
			core::rect<s32>(150,20,470,60));

		text->setOverrideColor(video::SColor(100,255,255,255));
	}
	
	\endcode

	Nearly finished. Now we need to draw everything. Every frame, we draw
	the scene twice. Once from the fixed camera into the render target
	texture and once as usual. When rendering into the render target, we
	need to disable the visibilty of the test cube, because it has the
	render target texture applied to it. That's it, wasn't too complicated
	I hope. :)
	\code

	int lastFPS = -1;

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

		if (rt)
		{
			// draw scene into render target
			
			// set render target texture
			driver->setRenderTarget(rt, true, true, video::SColor(0,0,0,255));

			// make cube invisible and set fixed camera as active camera
			test->setVisible(false);
			smgr->setActiveCamera(fixedCam);

			// draw whole scene into render buffer
			smgr->drawAll();

			// set back old render target
			// The buffer might have been distorted, so clear it
			driver->setRenderTarget(0, true, true, 0);

			// make the cube visible and set the user controlled camera as active one
			test->setVisible(true);
			smgr->setActiveCamera(fpsCamera);
		}
		
		// draw scene normally
		smgr->drawAll();
		env->drawAll();

		driver->endScene();

		// display frames per second in window title
		int fps = driver->getFPS();
		if (lastFPS != fps)
		{
			core::stringw str = L"Irrlicht Engine - Render to Texture and Specular Highlights example";
			str += " FPS:";
			str += fps;

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

	device->drop(); // drop device
	return 0;
}

\endcode

**/
/** \page example014 Tutorial 14: Win32 Window
 \image html "014shot.jpg"
 \image latex "014shot.jpg"

This example only runs under MS Windows and demonstrates that Irrlicht can
render inside a win32 window. MFC and .NET Windows.Forms windows are possible,
too.

In the begining, we create a windows window using the windows API. I'm not
going to explain this code, because it is windows specific. See the MSDN or a
windows book for details.
\code

#include <irrlicht.h>
#ifndef _IRR_WINDOWS_
#error Windows only example
#else
#include <windows.h> // this example only runs with windows
#include <iostream>

using namespace irr;

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

HWND hOKButton;
HWND hWnd;

static LRESULT CALLBACK CustomWndProc(HWND hWnd, UINT message,
		WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_COMMAND:
		{
			HWND hwndCtl = (HWND)lParam;
			int code = HIWORD(wParam);

			if (hwndCtl == hOKButton)
			{
				DestroyWindow(hWnd);
				PostQuitMessage(0);
				return 0;
			}
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}


\endcode

   Now ask for the driver and create the Windows specific window.
\code
int main()
//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hpre, LPSTR cmd, int cc)
{
	// ask user for driver

	video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D8;

	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 key;
	std::cin >> key;

	switch(key)
	{
		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;
	}

	printf("Select the render window (some dead window may exist too):\n"\
		" (a) Window with button (via CreationParam)\n"\
		" (b) Window with button (via beginScene)\n"\
		" (c) Own Irrlicht window (default behavior)\n"\
		" (otherKey) exit\n\n");

	std::cin >> key;
	if (key != 'a' && key != 'b' && key != 'c')
		return 1;

	HINSTANCE hInstance = 0;
	// create dialog

	const char* Win32ClassName = "CIrrlichtWindowsTestDialog";

	WNDCLASSEX wcex;
	wcex.cbSize			= sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)CustomWndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= DLGWINDOWEXTRA;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW);
	wcex.lpszMenuName	= 0;
	wcex.lpszClassName	= Win32ClassName;
	wcex.hIconSm		= 0;

	RegisterClassEx(&wcex);

	DWORD style = WS_SYSMENU | WS_BORDER | WS_CAPTION |
		WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX;

	int windowWidth = 440;
	int windowHeight = 380;

	hWnd = CreateWindow( Win32ClassName, "Irrlicht Win32 window example",
		style, 100, 100, windowWidth, windowHeight,
		NULL, NULL, hInstance, NULL);

	RECT clientRect;
	GetClientRect(hWnd, &clientRect);
	windowWidth = clientRect.right;
	windowHeight = clientRect.bottom;

	// create ok button

	hOKButton = CreateWindow("BUTTON", "OK - Close", WS_CHILD | WS_VISIBLE | BS_TEXT,
		windowWidth - 160, windowHeight - 40, 150, 30, hWnd, NULL, hInstance, NULL);

	// create some text

	CreateWindow("STATIC", "This is Irrlicht running inside a standard Win32 window.\n"\
		"Also mixing with MFC and .NET Windows.Forms is possible.",
		WS_CHILD | WS_VISIBLE, 20, 20, 400, 40, hWnd, NULL, hInstance, NULL);

	// create window to put irrlicht in

	HWND hIrrlichtWindow = CreateWindow("BUTTON", "",
			WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
			50, 80, 320, 220, hWnd, NULL, hInstance, NULL);
	video::SExposedVideoData videodata((key=='b')?hIrrlichtWindow:0);

	\endcode

	So now that we have some window, we can create an Irrlicht device
	inside of it. We use Irrlicht createEx() function for this. We only
	need the handle (HWND) to that window, set it as windowsID parameter
	and start up the engine as usual. That's it.
	\code
	// create irrlicht device in the button window

	irr::SIrrlichtCreationParameters param;
	param.DriverType = driverType;
	if (key=='a')
		param.WindowId = reinterpret_cast<void*>(hIrrlichtWindow);

	irr::IrrlichtDevice* device = irr::createDeviceEx(param);

	// setup a simple 3d scene

	irr::scene::ISceneManager* smgr = device->getSceneManager();
	video::IVideoDriver* driver = device->getVideoDriver();

	if (driverType==video::EDT_OPENGL)
	{
		HDC HDc=GetDC(hIrrlichtWindow);
		PIXELFORMATDESCRIPTOR pfd={0};
		pfd.nSize=sizeof(PIXELFORMATDESCRIPTOR);
		int pf = GetPixelFormat(HDc);
		DescribePixelFormat(HDc, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
		pfd.dwFlags |= PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
		pfd.cDepthBits=16;
		pf = ChoosePixelFormat(HDc, &pfd);
		SetPixelFormat(HDc, pf, &pfd);
		videodata.OpenGLWin32.HDc = HDc;
		videodata.OpenGLWin32.HRc=wglCreateContext(HDc);
		wglShareLists((HGLRC)driver->getExposedVideoData().OpenGLWin32.HRc, (HGLRC)videodata.OpenGLWin32.HRc);
	}
	scene::ICameraSceneNode* cam = smgr->addCameraSceneNode();
	cam->setTarget(core::vector3df(0,0,0));

	scene::ISceneNodeAnimator* anim =
		smgr->createFlyCircleAnimator(core::vector3df(0,15,0), 30.0f);
	cam->addAnimator(anim);
	anim->drop();

	scene::ISceneNode* cube = smgr->addCubeSceneNode(20);

	cube->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
	cube->setMaterialTexture(1, driver->getTexture("../../media/water.jpg"));
	cube->setMaterialFlag( video::EMF_LIGHTING, false );
	cube->setMaterialType( video::EMT_REFLECTION_2_LAYER );

	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"));

	// show and execute dialog

	ShowWindow(hWnd , SW_SHOW);
	UpdateWindow(hWnd);

	// do message queue

	\endcode

	Now the only thing missing is the drawing loop using
	IrrlichtDevice::run(). We do this as usual. But instead of this, there
	is another possibility: You can also simply use your own message loop
	using GetMessage, DispatchMessage and whatever. Calling
	Device->run() will cause Irrlicht to dispatch messages internally too.
	You need not call Device->run() if you want to do your own message
	dispatching loop, but Irrlicht will not be able to fetch user input
	then and you have to do it on your own using the window messages,
	DirectInput, or whatever.
	\code

	while (device->run())
	{
		driver->beginScene(true, true, 0, videodata);
		smgr->drawAll();
		driver->endScene();
	}

	\endcode

	The alternative, own message dispatching loop without Device->run()
	would look like this:
	\code

	\endcode MSG msg;
	while (true)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);

			if (msg.message == WM_QUIT)
				break;
		}

		// advance virtual time
		device->getTimer()->tick();

		// draw engine picture
		driver->beginScene(true, true, 0, (key=='c')?hIrrlichtWindow:0);
		smgr->drawAll();
		driver->endScene();
	}\code

	device->closeDevice();
	device->drop();

	return 0;
}
#endif // if windows

\endcode

That's it, Irrlicht now runs in your own windows window.
**/
/** \page example015 Tutorial 15: Loading Scenes from .irr Files
 \image html "015shot.jpg"
 \image latex "015shot.jpg"

Since version 1.1, Irrlicht is able to save and load
the full scene graph into an .irr file, an xml based
format. There is an editor available to edit
those files, named irrEdit (http://www.ambiera.com/irredit)
which can also be used as world and particle editor.
This tutorial shows how to use .irr files.

Lets start: Create an Irrlicht device and setup the window.
\code

#include <irrlicht.h>
#include <iostream>
using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

int main(int argc, char** argv)
{
	// ask user for driver

	video::E_DRIVER_TYPE driverType;

	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;
	}

	// create device and exit if creation failed

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

	if (device == 0)
		return 1; // could not create selected driver.

	device->setWindowCaption(L"Load .irr file example");

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

	\endcode

	Now load our .irr file.
	.irr files can store the whole scene graph including animators,
	materials and particle systems. And there is also the possibility to
	store arbitrary user data for every scene node in that file. To keep
	this example simple, we are simply loading the scene here. See the
	documentation at ISceneManager::loadScene and ISceneManager::saveScene
	for more information. So to load and display a complicated huge scene,
	we only need a single call to loadScene().
	\code

	// load the scene
	if (argc>1)
		smgr->loadScene(argv[1]);
	else
		smgr->loadScene("../../media/example.irr");

	\endcode

	Now we'll create a camera, and give it a collision response animator
	that's built from the mesh nodes in the scene we just loaded.
	\code
	scene::ICameraSceneNode * camera = smgr->addCameraSceneNodeFPS(0, 50.f, 0.1f);

	// Create a meta triangle selector to hold several triangle selectors.
	scene::IMetaTriangleSelector * meta = smgr->createMetaTriangleSelector();

	\endcode

	Now we will find all the nodes in the scene and create triangle
	selectors for all suitable nodes.  Typically, you would want to make a
	more informed decision about which nodes to performs collision checks
	on; you could capture that information in the node name or Id.
	\code
	core::array<scene::ISceneNode *> nodes;
	smgr->getSceneNodesFromType(scene::ESNT_ANY, nodes); // Find all nodes

	for (u32 i=0; i < nodes.size(); ++i)
	{
		scene::ISceneNode * node = nodes[i];
		scene::ITriangleSelector * selector = 0;

		switch(node->getType())
		{
		case scene::ESNT_CUBE:
		case scene::ESNT_ANIMATED_MESH:
			// Because the selector won't animate with the mesh,
			// and is only being used for camera collision, we'll just use an approximate
			// bounding box instead of ((scene::IAnimatedMeshSceneNode*)node)->getMesh(0)
			selector = smgr->createTriangleSelectorFromBoundingBox(node);
		break;

		case scene::ESNT_MESH:
		case scene::ESNT_SPHERE: // Derived from IMeshSceneNode
			selector = smgr->createTriangleSelector(((scene::IMeshSceneNode*)node)->getMesh(), node);
			break;

		case scene::ESNT_TERRAIN:
			selector = smgr->createTerrainTriangleSelector((scene::ITerrainSceneNode*)node);
			break;

		case scene::ESNT_OCTREE:
			selector = smgr->createOctreeTriangleSelector(((scene::IMeshSceneNode*)node)->getMesh(), node);
			break;

		default:
			// Don't create a selector for this node type
			break;
		}

		if(selector)
		{
			// Add it to the meta selector, which will take a reference to it
			meta->addTriangleSelector(selector);
			// And drop my reference to it, so that the meta selector owns it.
			selector->drop();
		}
	}

	\endcode

	Now that the mesh scene nodes have had triangle selectors created and added
	to the meta selector, create a collision response animator from that meta selector.
	\code
	scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
		meta, camera, core::vector3df(5,5,5),
		core::vector3df(0,0,0));
	meta->drop(); // I'm done with the meta selector now

	camera->addAnimator(anim);
	anim->drop(); // I'm done with the animator now

	// And set the camera position so that it doesn't start off stuck in the geometry
	camera->setPosition(core::vector3df(0.f, 20.f, 0.f));

	// Point the camera at the cube node, by finding the first node of type ESNT_CUBE
	scene::ISceneNode * cube = smgr->getSceneNodeFromType(scene::ESNT_CUBE);
	if(cube)
		camera->setTarget(cube->getAbsolutePosition());

	\endcode

	That's it. Draw everything and finish as usual.
	\code

	int lastFPS = -1;

	while(device->run())
	if (device->isWindowActive())
	{
		driver->beginScene(true, true, video::SColor(0,200,200,200));
		smgr->drawAll();
		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps)
		{
			core::stringw str = L"Load Irrlicht File example - Irrlicht Engine [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;

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

	}

	device->drop();

	return 0;
}

\endcode

**/
/** \page example016 Tutorial 16: Quake3 Map Shader Support
 \image html "016shot.jpg"
 \image latex "016shot.jpg"

This Tutorial shows how to load a Quake 3 map into the
engine, create a SceneNode for optimizing the speed of
rendering and how to create a user controlled camera.

Lets start like the HelloWorld example: We include
the irrlicht header files and an additional file to be able
to ask the user for a driver type using the console.
\code
#include <irrlicht.h>
#include <iostream>

\endcode

	define which Quake3 Level should be loaded
\code
#define IRRLICHT_QUAKE3_ARENA
//#define ORIGINAL_QUAKE3_ARENA
//#define CUSTOM_QUAKE3_ARENA
//#define SHOW_SHADER_NAME

#ifdef ORIGINAL_QUAKE3_ARENA
	#define QUAKE3_STORAGE_FORMAT	addFolderFileArchive
	#define QUAKE3_STORAGE_1		"/baseq3/"
	#ifdef CUSTOM_QUAKE3_ARENA
		#define QUAKE3_STORAGE_2	"/cf/"
		#define QUAKE3_MAP_NAME		"maps/cf.bsp"
	#else
		#define QUAKE3_MAP_NAME			"maps/q3dm8.bsp"
	#endif
#endif

#ifdef IRRLICHT_QUAKE3_ARENA
	#define QUAKE3_STORAGE_FORMAT	addZipFileArchive
	#define QUAKE3_STORAGE_1	"../../media/map-20kdm2.pk3"
	#define QUAKE3_MAP_NAME			"maps/20kdm2.bsp"
#endif


\endcode

As already written in the HelloWorld example, in the Irrlicht
Engine, everything can be found in the namespace 'irr'.
To get rid of the irr:: in front of the name of every class,
we tell the compiler that we use that namespace from now on,
and we will not have to write that 'irr::'.
There are 5 other sub namespaces 'core', 'scene', 'video',
'io' and 'gui'. Unlike in the HelloWorld example,
we do not a 'using namespace' for these 5 other namespaces
because in this way you will see what can be found in which
namespace. But if you like, you can also include the namespaces
like in the previous example. Code just like you want to.
\code
using namespace irr;
using namespace scene;

\endcode

Again, to be able to use the Irrlicht.DLL file, we need to link with the
Irrlicht.lib. We could set this option in the project settings, but
to make it easy, we use a pragma comment lib:
\code
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif


//! produces a serie of screenshots
class CScreenShotFactory : public IEventReceiver
{
public:

	CScreenShotFactory( IrrlichtDevice *device, const c8 * templateName, ISceneNode* node )
		: Device(device), Number(0), FilenameTemplate(templateName), Node(node)
	{
		FilenameTemplate.replace ( '/', '_' );
		FilenameTemplate.replace ( '\\', '_' );
	}

	bool OnEvent(const SEvent& event)
	{
		// check if user presses the key F9
		if ((event.EventType == EET_KEY_INPUT_EVENT) &&
				event.KeyInput.PressedDown)
		{
			if (event.KeyInput.Key == KEY_F9)
			{
				video::IImage* image = Device->getVideoDriver()->createScreenShot();
				if (image)
				{
					c8 buf[256];
					snprintf(buf, 256, "%s_shot%04d.jpg",
							FilenameTemplate.c_str(),
							++Number);
					Device->getVideoDriver()->writeImageToFile(image, buf, 85 );
					image->drop();
				}
			}
			else
			if (event.KeyInput.Key == KEY_F8)
			{
				Node->setDebugDataVisible(scene::EDS_BBOX_ALL);
			}
		}
		return false;
	}

private:
	IrrlichtDevice *Device;
	u32 Number;
	core::stringc FilenameTemplate;
	ISceneNode* Node;
};


\endcode

Ok, lets start.
\code

int IRRCALLCONV main(int argc, char* argv[])
{
	\endcode

	Like in the HelloWorld example, we create an IrrlichtDevice with
	createDevice(). The difference now is that we ask the user to select
	which hardware accelerated driver to use. The Software device would be
	too slow to draw a huge Quake 3 map, but just for the fun of it, we make
	this decision possible too.
	\code

	// ask user for driver

	video::E_DRIVER_TYPE driverType;

	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;
	}

	// create device and exit if creation failed
	const core::dimension2du videoDim ( 800,600 );

	IrrlichtDevice *device = createDevice(driverType, videoDim, 32, false );

	if (device == 0)
		return 1; // could not create selected driver.

	const char* mapname=0;
	if (argc>2)
		mapname = argv[2];
	else
		mapname = QUAKE3_MAP_NAME;

	\endcode

	Get a pointer to the video driver and the SceneManager so that
	we do not always have to write device->getVideoDriver() and
	device->getSceneManager().
	\code
	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();
	gui::IGUIEnvironment* gui = device->getGUIEnvironment();

	//! add our private media directory to the file system
	device->getFileSystem()->addFolderFileArchive("../../media/");

	\endcode

	To display the Quake 3 map, we first need to load it. Quake 3 maps
	are packed into .pk3 files, which are nothing other than .zip files.
	So we add the .pk3 file to our FileSystem. After it was added,
	we are able to read from the files in that archive as they would
	directly be stored on disk.
	\code
	if (argc>2)
		device->getFileSystem()->QUAKE3_STORAGE_FORMAT (argv[1]);
	else
		device->getFileSystem()->QUAKE3_STORAGE_FORMAT ( QUAKE3_STORAGE_1 );
#ifdef QUAKE3_STORAGE_2
	device->getFileSystem()->QUAKE3_STORAGE_FORMAT ( QUAKE3_STORAGE_2 );
#endif



	// Quake3 Shader controls Z-Writing
	smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);

	\endcode

	Now we can load the mesh by calling getMesh(). We get a pointer returned
	to a IAnimatedMesh. As you know, Quake 3 maps are not really animated,
	they are only a huge chunk of static geometry with some materials
	attached. Hence the IAnimated mesh consists of only one frame,
	so we get the "first frame" of the "animation", which is our quake level
	and create an Octree scene node with it, using addOctreeSceneNode().
	The Octree optimizes the scene a little bit, trying to draw only geometry
	which is currently visible. An alternative to the Octree would be a
	AnimatedMeshSceneNode, which would draw always the complete geometry of
	the mesh, without optimization. Try it out: Write addAnimatedMeshSceneNode
	instead of addOctreeSceneNode and compare the primitives drawed by the
	video driver. (There is a getPrimitiveCountDrawed() method in the
	IVideoDriver class). Note that this optimization with the Octree is only
	useful when drawing huge meshes consisting of lots of geometry.
	\code
	scene::IQ3LevelMesh* mesh =
		(scene::IQ3LevelMesh*) smgr->getMesh(mapname);

	\endcode

		add the geometry mesh to the Scene ( polygon & patches )
		The Geometry mesh is optimised for faster drawing
	\code
	scene::ISceneNode* node = 0;
	if ( mesh )
	{
		scene::IMesh *geometry = mesh->getMesh(quake3::E_Q3_MESH_GEOMETRY);
//		node = smgr->addMeshSceneNode ( geometry );
		node = smgr->addOctreeSceneNode(geometry, 0, -1, 1024);
	}

	// create an event receiver for making screenshots
	CScreenShotFactory screenshotFactory ( device, mapname, node );
	device->setEventReceiver ( &screenshotFactory );

	\endcode

		now construct SceneNodes for each Shader
		The Objects are stored in the quake mesh scene::E_Q3_MESH_ITEMS
		and the Shader ID is stored in the MaterialParameters
		mostly dark looking skulls and moving lava.. or green flashing tubes?
	\code
	if ( mesh )
	{
		// the additional mesh can be quite huge and is unoptimized
		scene::IMesh * additional_mesh = mesh->getMesh ( quake3::E_Q3_MESH_ITEMS );

#ifdef SHOW_SHADER_NAME
		gui::IGUIFont *font = device->getGUIEnvironment()->getFont("../../media/fontlucida.png");
		u32 count = 0;
#endif

		for ( u32 i = 0; i!= additional_mesh->getMeshBufferCount (); ++i )
		{
			IMeshBuffer *meshBuffer = additional_mesh->getMeshBuffer ( i );
			const video::SMaterial &material = meshBuffer->getMaterial();

			//! The ShaderIndex is stored in the material parameter
			s32 shaderIndex = (s32) material.MaterialTypeParam2;

			// the meshbuffer can be rendered without additional support, or it has no shader
			const quake3::IShader *shader = mesh->getShader ( shaderIndex );
			if ( 0 == shader )
			{
				continue;
			}

			// we can dump the shader to the console in its
			// original but already parsed layout in a pretty
			// printers way.. commented out, because the console
			// would be full...
			// quake3::dumpShader ( Shader );

#ifndef SHOW_SHADER_NAME
			smgr->addQuake3SceneNode ( meshBuffer, shader );
#else
			count += 1;

			node = smgr->addQuake3SceneNode ( meshBuffer, shader );

			core::stringw name( node->getName() );
			node = smgr->addBillboardTextSceneNode(
					font,
					name.c_str(),
					node,
					core::dimension2d<f32>(80.0f, 8.0f),
					core::vector3df(0, 10, 0)
					);
#endif
		}


	}

	\endcode

	Now we only need a Camera to look at the Quake 3 map. And we want to
	create a user controlled camera. There are some different cameras
	available in the Irrlicht engine. For example the Maya Camera which can
	be controlled compareable to the camera in Maya: Rotate with left mouse
	button pressed, Zoom with both buttons pressed, translate with right
	mouse button pressed. This could be created with
	addCameraSceneNodeMaya(). But for this example, we want to create a
	camera which behaves like the ones in first person shooter games (FPS).
	\code

	scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();

	\endcode

		so we need a good starting Position in the level.
		we can ask the Quake3 Loader for all entities with class_name
		"info_player_deathmatch"
		we choose a random launch

	\code
	if ( mesh )
	{
		quake3::tQ3EntityList &entityList = mesh->getEntityList ();

		quake3::IEntity search;
		search.name = "info_player_deathmatch";

		s32 index = entityList.binary_search ( search );
		if ( index >= 0 )
		{
			const quake3::SVarGroup *group;
			s32 notEndList;
			do
			{
				group = entityList[ index ].getGroup(1);

				u32 parsepos = 0;
				core::vector3df pos =
					quake3::getAsVector3df ( group->get ( "origin" ), parsepos );

				parsepos = 0;
				f32 angle = quake3::getAsFloat ( group->get ( "angle"), parsepos );

				core::vector3df target ( 0.f, 0.f, 1.f );
				target.rotateXZBy ( angle, core::vector3df () );

				camera->setPosition ( pos );
				camera->setTarget ( pos + target );

				index += 1;
\endcode

				notEndList = (	index < (s32) entityList.size () &&
								entityList[index].name == search.name &&
								(device->getTimer()->getRealTime() >> 3 ) & 1
							);
\code
				notEndList = index == 2;
			} while ( notEndList );
		}

	}

	\endcode

	The mouse cursor needs not to be visible, so we make it invisible.
	\code

	device->getCursorControl()->setVisible(false);

	// load the engine logo
	gui->addImage(driver->getTexture("irrlichtlogo2.png"),
			core::position2d<s32>(10, 10));

	// show the driver logo
	core::position2di pos ( videoDim.Width - 128, videoDim.Height - 64 );

	switch ( driverType )
	{
		case video::EDT_BURNINGSVIDEO:
			gui->addImage(driver->getTexture("burninglogo.png"),pos);
			break;
		case video::EDT_OPENGL:
			gui->addImage(driver->getTexture("opengllogo.png"),pos);
			break;
		case video::EDT_DIRECT3D8:
		case video::EDT_DIRECT3D9:
			gui->addImage(driver->getTexture("directxlogo.png"),pos);
			break;
	}

	\endcode

	We have done everything, so lets draw it. We also write the current
	frames per second and the drawn primitives to the caption of the
	window. The 'if (device->isWindowActive())' line is optional, but
	prevents the engine render to set the position of the mouse cursor
	after task switching when other program are active.
	\code
	int lastFPS = -1;

	while(device->run())
	if (device->isWindowActive())
	{
		driver->beginScene(true, true, video::SColor(255,20,20,40));
		smgr->drawAll();
		gui->drawAll();

		driver->endScene();

		int fps = driver->getFPS();

		//if (lastFPS != fps)
		{
			io::IAttributes * attr = smgr->getParameters();

			s32 calls = attr->getAttributeAsInt ( "calls" );
			s32 culled = attr->getAttributeAsInt ( "culled" );

			core::stringw str = L"Q3 [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;
			str += " Cull:";
			str += calls;
			str += "/";
			str += culled;
			str += " Draw: ";
			str += attr->getAttributeAsInt ( "drawn_solid" );
			str += "/";
			str += attr->getAttributeAsInt ( "drawn_transparent" );
			str += "/";
			str += attr->getAttributeAsInt ( "drawn_transparent_effect" );

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

	\endcode

	In the end, delete the Irrlicht device.
	\code
	device->drop();

	return 0;
}

\endcode

**/
/** \page example017 Tutorial 17: Helloworld mobile
 \image html "017shot.jpg"
 \image latex "017shot.jpg"
	This example show Hello World for Windows mobile.
	It compiles on other platform too. The only differences between the original
	examples are. You need a GUI, because otherwise you can't quit the application.
	You need a Filesystem, which is relative based to your executable.
\code

#include <irrlicht.h>

#if defined ( _IRR_WINDOWS_ )
	#include <windows.h>
#endif

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

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

class EventReceiver_basic : public IEventReceiver
{
private:
	IrrlichtDevice *Device;
public:
	EventReceiver_basic ( IrrlichtDevice *device ): Device ( device ) {}

	virtual bool OnEvent(const SEvent& event)
	{
		if (event.EventType == EET_GUI_EVENT)
		{
			s32 id = event.GUIEvent.Caller->getID();

			switch(event.GUIEvent.EventType)
			{
				case EGET_BUTTON_CLICKED:
				if (id == 2)
				{
					Device->closeDevice();
					return true;
				} break;
			}
		}

		return false;
	}
};

class CSampleSceneNode : public ISceneNode
{
	aabbox3d<f32> Box;
	S3DVertex Vertices[4];
	SMaterial Material;
public:

	CSampleSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id)
		: ISceneNode(parent, mgr, id)
	{
		Material.Wireframe = false;
		Material.Lighting = false;

		Vertices[0] = S3DVertex(0,0,10, 1,1,0, SColor(255,0,255,255), 0, 1);
		Vertices[1] = S3DVertex(10,0,-10, 1,0,0, SColor(255,255,0,255), 1, 1);
		Vertices[2] = S3DVertex(0,20,0, 0,1,1, SColor(255,255,255,0), 1, 0);
		Vertices[3] = S3DVertex(-10,0,-10, 0,0,1, SColor(255,0,255,0), 0, 0);
		Box.reset(Vertices[0].Pos);
		for (s32 i=1; i<4; ++i)
			Box.addInternalPoint(Vertices[i].Pos);
	}
	virtual void OnRegisterSceneNode()
	{
		if (IsVisible)
			SceneManager->registerNodeForRendering(this);

		ISceneNode::OnRegisterSceneNode();
	}

	virtual void render()
	{
		u16 indices[] = {	0,2,3, 2,1,3, 1,0,3, 2,0,1	};
		IVideoDriver* driver = SceneManager->getVideoDriver();

		driver->setMaterial(Material);
		driver->setTransform(ETS_WORLD, AbsoluteTransformation);
		driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
	}

	virtual const aabbox3d<f32>& getBoundingBox() const
	{
		return Box;
	}

	virtual u32 getMaterialCount()
	{
		return 1;
	}

	virtual SMaterial& getMaterial(u32 i)
	{
		return Material;
	}	
};

\endcode !
	Startup a Windows Mobile Device
\code
IrrlichtDevice *startup()
{
	// both software and burnings video can be used
	E_DRIVER_TYPE driverType = EDT_SOFTWARE; // EDT_BURNINGSVIDEO;

	// create device
	IrrlichtDevice *device = 0;

#if defined (_IRR_USE_WINDOWS_CE_DEVICE_)
	// set to standard mobile fullscreen 240x320
	device = createDevice(driverType, dimension2d<u32>(240, 320), 16, true );
#else
	// on PC. use window mode
	device = createDevice(driverType, dimension2d<u32>(240, 320), 16, false );
#endif		
	if ( 0 == device )
		return 0;

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();

	// set the filesystem relative to the executable
#if defined (_IRR_WINDOWS_)
	{
		wchar_t buf[255];
		GetModuleFileNameW ( 0, buf, 255 );

		io::path base = buf;
		base = base.subString ( 0, base.findLast ( '\\' ) + 1 );
		device->getFileSystem()->addFileArchive ( base );
	}
#endif

	IGUIStaticText *text = guienv->addStaticText(L"FPS: 25",
		rect<s32>(140,15,200,30), false, false, 0, 100 );

	guienv->addButton(core::rect<int>(200,10,238,30), 0, 2, L"Quit");

	// add irrlicht logo
	guienv->addImage(driver->getTexture("../../media/irrlichtlogo3.png"),
					core::position2d<s32>(0,-2));
	return device;
}

\endcode !
\code
int run ( IrrlichtDevice *device )
{
	while(device->run())
	if (device->isWindowActive())
	{
		device->getVideoDriver()->beginScene(true, true, SColor(0,100,100,100));
		device->getSceneManager()->drawAll();
		device->getGUIEnvironment()->drawAll();
		device->getVideoDriver()->endScene ();

		IGUIElement *stat = device->getGUIEnvironment()->
			getRootGUIElement()->getElementFromId ( 100 );
		if ( stat )
		{
			stringw str = L"FPS: ";
			str += (s32)device->getVideoDriver()->getFPS();

			stat->setText ( str.c_str() );
		}
	}

	device->drop();
	return 0;
}

\endcode !
\code
int example_customscenenode()
{
	// create device
	IrrlichtDevice *device = startup();
	if (device == 0)
		return 1; // could not create selected driver.

	// create engine and camera
	EventReceiver_basic receiver(device);
	device->setEventReceiver(&receiver);
	
	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();


	smgr->addCameraSceneNode(0, vector3df(0,-40,0), vector3df(0,0,0));

	CSampleSceneNode *myNode = 
		new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);

	ISceneNodeAnimator* anim = 
		smgr->createRotationAnimator(vector3df(0.8f, 0, 0.8f));

	if(anim)
	{
		myNode->addAnimator(anim);
		anim->drop();
		anim = 0; // As I shouldn't refer to it again, ensure that I can't
	}

	myNode->drop();
	myNode = 0; // As I shouldn't refer to it again, ensure that I can't

	return run ( device );
}

class EventReceiver_terrain : public IEventReceiver
{
public:

	EventReceiver_terrain(IrrlichtDevice *device, scene::ISceneNode* terrain, scene::ISceneNode* skybox, scene::ISceneNode* skydome) :
		Device ( device ), Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true)
	{
		Skybox->setVisible(true);
		Skydome->setVisible(false);
	}

	bool OnEvent(const SEvent& event)
	{
		if (event.EventType == EET_GUI_EVENT)
		{
			s32 id = event.GUIEvent.Caller->getID();

			switch(event.GUIEvent.EventType)
			{
				case EGET_BUTTON_CLICKED:
				if (id == 2)
				{
					Device->closeDevice();
					return true;
				} break;
			}
		}

		// check if user presses the key 'W' or 'D'
		if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
		{
			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;
			default:
				break;
			}
		}

		return false;
	}

private:
	IrrlichtDevice *Device;
	scene::ISceneNode* Terrain;
	scene::ISceneNode* Skybox;
	scene::ISceneNode* Skydome;
	bool showBox;
};


\endcode

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.
\code
int example_terrain()
{
	// create device
	IrrlichtDevice *device = startup();
	if (device == 0)
		return 1; // could not create selected driver.
	
	\endcode

	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.
	\code

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


	//set other font
	//env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));

	// add some help text
	env->addStaticText(
		L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map\nPress 'S' to toggle skybox/skydome",
		core::rect<s32>(5,250,235,320), true, true, 0, -1, true);

	// add camera
	scene::ICameraSceneNode* 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);

	\endcode

	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.
	\code

	// add terrain scene node
	scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
		"../../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
		);

	if ( terrain )
	{
		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);
		//terrain->setDebugDataVisible ( true );

		\endcode

		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.
		\code

		// create triangle selector for the terrain	
		scene::ITriangleSelector* selector
			= smgr->createTerrainTriangleSelector(terrain, 0);
		terrain->setTriangleSelector(selector);

		// create collision response animator and attach it to the camera
		scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
			selector, camera, core::vector3df(60,100,60),
			core::vector3df(0,0,0),
			core::vector3df(0,50,0));
		selector->drop();
		camera->addAnimator(anim);
		anim->drop();

		\endcode  If you need access to the terrain data you can also do this directly via the following code fragment.
		\code
		scene::CDynamicMeshBuffer* buffer = new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
		terrain->getMeshBufferForLOD(*buffer, 0);
		video::S3DVertex2TCoords* data = (video::S3DVertex2TCoords*)buffer->getVertexBuffer().getData();
		// Work on data or get the IndexBuffer with a similar call.
		buffer->drop(); // When done drop the buffer again.
	}

	\endcode

	To make the user be able to switch between normal and wireframe mode,
	we create an instance of the event reciever 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'.
	\code

	// create skybox and skydome
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

	scene::ISceneNode* skybox=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"));
	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
	EventReceiver_terrain receiver( device, terrain, skybox, skydome);
	device->setEventReceiver(&receiver);

	return run ( device );
}

\endcode

\code
int example_helloworld()
{
	// create device
	IrrlichtDevice *device = startup();
	if (device == 0)
		return 1; // could not create selected driver.

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();

	IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
	if (!mesh)
	{
		device->drop();
		return 1;
	}
	IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );

	\endcode

	To let the mesh look a little bit nicer, we change its material. We
	disable lighting because we do not have a dynamic light in here, and
	the mesh would be totally black otherwise. Then we set the frame loop,
	such that the predefined STAND animation is used. And last, we apply a
	texture to the mesh. Without it the mesh would be drawn using only a
	color.
	\code
	if (node)
	{
		node->setMaterialFlag(EMF_LIGHTING, false);
		node->setMD2Animation(scene::EMAT_STAND);
		node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
	}

	\endcode

	To look at the mesh, we place a camera into 3d space at the position
	(0, 30, -40). The camera looks from there to (0,5,0), which is
	approximately the place where our md2 model is.
	\code
	smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

	EventReceiver_basic receiver(device);
	device->setEventReceiver(&receiver);

	return run ( device );

}

#if defined (_IRR_USE_WINDOWS_CE_DEVICE_)
	#pragma comment(linker, "/subsystem:WINDOWSCE /ENTRY:main") 
#elif defined (_IRR_WINDOWS_)
	#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

\endcode

\code
int main()
{
	example_helloworld ();
	example_customscenenode();
	//example_terrain();
}

\endcode

**/
/** \page example018 Tutorial 18: Splitscreen
 \image html "018shot.jpg"
 \image latex "018shot.jpg"

A tutorial by Max Winkel.

In this tutorial we'll learn how to use splitscreen (e.g. for racing-games)
with Irrlicht. We'll create a viewport divided
into 4 parts, wtih 3 fixed cameras and one user-controlled.

Ok, let's start with the headers (I think there's
nothing to say about it)
\code

#include <irrlicht.h>
#include <iostream>

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

//Namespaces for the engine
using namespace irr;
using namespace core;
using namespace video;
using namespace scene;

\endcode

Now we'll define the resolution in a constant for use in
initializing the device and setting up the viewport. In addition
we set up a global variable saying splitscreen is active or not.
\code
//Resolution
const int ResX=800;
const int ResY=600;
const bool fullScreen=false;

//Use SplitScreen?
bool SplitScreen=true;

\endcode

Now we need four pointers to our cameras which are created later:
\code
//cameras
ICameraSceneNode *camera[4]={0,0,0,0};
\endcode

In our event-receiver we switch the SplitScreen-variable,
whenever the user press the S-key. All other events are sent
to the FPS camera.
\code

class MyEventReceiver : public IEventReceiver
{
	public:
		virtual bool OnEvent(const SEvent& event)
		{
			//Key S enables/disables SplitScreen
			if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
				event.KeyInput.Key == KEY_KEY_S && event.KeyInput.PressedDown)
			{
				SplitScreen = !SplitScreen;
				return true;
			}
			//Send all other events to camera4
			if (camera[3])
				return camera[3]->OnEvent(event);
			return false;
		}
};

\endcode

Ok, now the main-function:
First, we initialize the device, get the SourceManager and
VideoDriver, load an animated mesh from .md2 and a map from
.pk3. Because that's old stuff, I won't explain every step.
Just take care of the maps position.
\code
int main(int argc, char** argv)
{
	video::E_DRIVER_TYPE driverType;

	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;
	}

	//Instance of the EventReceiver
	MyEventReceiver receiver;

	//Initialise the engine
	IrrlichtDevice *device = createDevice(driverType,
			dimension2du(ResX,ResY), 32, fullScreen,
			false, false, &receiver);
	if (!device)
		return 1;

	ISceneManager *smgr = device->getSceneManager();
	IVideoDriver *driver = device->getVideoDriver();

	//Load model
	IAnimatedMesh *model = smgr->getMesh("../../media/sydney.md2");
	if (!model)
		return 1;
	IAnimatedMeshSceneNode *model_node = smgr->addAnimatedMeshSceneNode(model);
	//Load texture
	if (model_node)
	{
		ITexture *texture = driver->getTexture("../../media/sydney.bmp");
		model_node->setMaterialTexture(0,texture);
		model_node->setMD2Animation(scene::EMAT_RUN);
		//Disable lighting (we've got no light)
		model_node->setMaterialFlag(EMF_LIGHTING,false);
	}

	//Load map
	device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");
	IAnimatedMesh *map = smgr->getMesh("20kdm2.bsp");
	if (map)
	{
		ISceneNode *map_node = smgr->addOctreeSceneNode(map->getMesh(0));
		//Set position
		map_node->setPosition(vector3df(-850,-220,-850));
	}

\endcode

Now we create our four cameras. One is looking at the model
from the front, one from the top and one from the side. In
addition there's a FPS-camera which can be controlled by the
user.
\code
	// Create 3 fixed and one user-controlled cameras
	//Front
	camera[0] = smgr->addCameraSceneNode(0, vector3df(50,0,0), vector3df(0,0,0));
	//Top
	camera[1] = smgr->addCameraSceneNode(0, vector3df(0,50,0), vector3df(0,0,0));
	//Left
	camera[2] = smgr->addCameraSceneNode(0, vector3df(0,0,50), vector3df(0,0,0));
	//User-controlled
	camera[3] = smgr->addCameraSceneNodeFPS();
	// don't start at sydney's position
	if (camera[3])
		camera[3]->setPosition(core::vector3df(-50,0,-50));

\endcode

Create a variable for counting the fps and hide the mouse:
\code
	//Hide mouse
	device->getCursorControl()->setVisible(false);
	//We want to count the fps
	int lastFPS = -1;

\endcode

There wasn't much new stuff - till now!
Only by defining four cameras, the game won't be splitscreen.
To do this you need several steps:
  - Set the viewport to the whole screen
  - Begin a new scene (Clear screen)

  - The following 3 steps are repeated for every viewport in the splitscreen
    - Set the viewport to the area you wish
    - Activate the camera which should be "linked" with the viewport
    - Render all objects

  - If you have a GUI:
    - Set the viewport the whole screen
    - Display the GUI
  - End scene

Sounds a little complicated, but you'll see it isn't:
\code

	while(device->run())
	{
		//Set the viewpoint to the whole screen and begin scene
		driver->setViewPort(rect<s32>(0,0,ResX,ResY));
		driver->beginScene(true,true,SColor(255,100,100,100));
		//If SplitScreen is used
		if (SplitScreen)
		{
			//Activate camera1
			smgr->setActiveCamera(camera[0]);
			//Set viewpoint to the first quarter (left top)
			driver->setViewPort(rect<s32>(0,0,ResX/2,ResY/2));
			//Draw scene
			smgr->drawAll();
			//Activate camera2
			smgr->setActiveCamera(camera[1]);
			//Set viewpoint to the second quarter (right top)
			driver->setViewPort(rect<s32>(ResX/2,0,ResX,ResY/2));
			//Draw scene
			smgr->drawAll();
			//Activate camera3
			smgr->setActiveCamera(camera[2]);
			//Set viewpoint to the third quarter (left bottom)
			driver->setViewPort(rect<s32>(0,ResY/2,ResX/2,ResY));
			//Draw scene
			smgr->drawAll();
			//Set viewport the last quarter (right bottom)
			driver->setViewPort(rect<s32>(ResX/2,ResY/2,ResX,ResY));
		}
		//Activate camera4
		smgr->setActiveCamera(camera[3]);
		//Draw scene
		smgr->drawAll();
		driver->endScene();

		\endcode

		As you can probably see, the image is rendered for every
		viewport seperately. That means, that you'll loose much performance.
		Ok, if you're aksing "How do I have to set the viewport
		to get this or that screen?", don't panic. It's really
		easy: In the rect-function you define 4 coordinates:
		- X-coordinate of the corner left top
		- Y-coordinate of the corner left top
		- X-coordinate of the corner right bottom
		- Y-coordinate of the corner right bottom

		That means, if you want to split the screen into 2 viewports
		you would give the following coordinates:
		- 1st viewport: 0,0,ResX/2,ResY
		- 2nd viewport: ResX/2,0,ResX,ResY

		If you didn't fully understand, just play arround with the example
		to check out what happens.

		Now we just view the current fps and shut down the engine,
		when the user wants to:
		\code
		//Get and show fps
		if (driver->getFPS() != lastFPS)
		{
			lastFPS = driver->getFPS();
			core::stringw tmp = L"Irrlicht SplitScreen-Example (FPS: ";
			tmp += lastFPS;
			tmp += ")";
			device->setWindowCaption(tmp.c_str());
		}
	}
	//Delete device
	device->drop();
	return 0;
}
\endcode

That's it! Just compile and play around with the program.
Note: With the S-Key you can switch between using splitscreen
and not.
**/

/** \page example019 Tutorial 19: Mouse and Joystick
 \image html "019shot.jpg"
 \image latex "019shot.jpg"

This tutorial builds on example 04.Movement which showed how to
handle keyboard events in Irrlicht.  Here we'll handle mouse events
and joystick events, if you have a joystick connected and a device
that supports joysticks.  These are currently Windows, Linux and SDL
devices.
\code

#ifdef _MSC_VER
// We'll define this to stop MSVC complaining about sprintf().
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "Irrlicht.lib")
#endif

#include <irrlicht.h>
#include <iostream>

using namespace irr;

\endcode

Just as we did in example 04.Movement, we'll store the latest state of the
mouse and the first joystick, updating them as we receive events.
\code
class MyEventReceiver : public IEventReceiver
{
public:
	// We'll create a struct to record info on the mouse state
	struct SMouseState
	{
		core::position2di Position;
		bool LeftButtonDown;
		SMouseState() : LeftButtonDown(false) { }
	} MouseState;

	// This is the one method that we have to implement
	virtual bool OnEvent(const SEvent& event)
	{
		// Remember the mouse state
		if (event.EventType == irr::EET_MOUSE_INPUT_EVENT)
		{
			switch(event.MouseInput.Event)
			{
			case EMIE_LMOUSE_PRESSED_DOWN:
				MouseState.LeftButtonDown = true;
				break;

			case EMIE_LMOUSE_LEFT_UP:
				MouseState.LeftButtonDown = false;
				break;

			case EMIE_MOUSE_MOVED:
				MouseState.Position.X = event.MouseInput.X;
				MouseState.Position.Y = event.MouseInput.Y;
				break;

			default:
				// We won't use the wheel
				break;
			}
		}

		// The state of each connected joystick is sent to us
		// once every run() of the Irrlicht device.  Store the
		// state of the first joystick, ignoring other joysticks.
		// This is currently only supported on Windows and Linux.
		if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT
			&& event.JoystickEvent.Joystick == 0)
		{
			JoystickState = event.JoystickEvent;
		}

		return false;
	}

	const SEvent::SJoystickEvent & GetJoystickState(void) const
	{
		return JoystickState;
	}

	const SMouseState & GetMouseState(void) const
	{
		return MouseState;
	}


	MyEventReceiver()
	{
	}

private:
	SEvent::SJoystickEvent JoystickState;
};


\endcode

The event receiver for keeping the pressed keys is ready, the actual responses
will be made inside the render loop, right before drawing the scene. So lets
just create an irr::IrrlichtDevice and the scene node we want to move. We also
create some other additional scene nodes, to show that there are also some
different possibilities to move and animate scene nodes.
\code
int main()
{
	// let user select driver type

	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 0;
	}

	// create device
	MyEventReceiver receiver;

	IrrlichtDevice* device = createDevice(driverType,
			core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);

	if (device == 0)
		return 1; // could not create selected driver.


	core::array<SJoystickInfo> joystickInfo;
	if(device->activateJoysticks(joystickInfo))
	{
		std::cout << "Joystick support is enabled and " << joystickInfo.size() << " joystick(s) are present." << std::endl;

		for(u32 joystick = 0; joystick < joystickInfo.size(); ++joystick)
		{
			std::cout << "Joystick " << joystick << ":" << std::endl;
			std::cout << "\tName: '" << joystickInfo[joystick].Name.c_str() << "'" << std::endl;
			std::cout << "\tAxes: " << joystickInfo[joystick].Axes << std::endl;
			std::cout << "\tButtons: " << joystickInfo[joystick].Buttons << std::endl;

			std::cout << "\tHat is: ";

			switch(joystickInfo[joystick].PovHat)
			{
			case SJoystickInfo::POV_HAT_PRESENT:
				std::cout << "present" << std::endl;
				break;

			case SJoystickInfo::POV_HAT_ABSENT:
				std::cout << "absent" << std::endl;
				break;

			case SJoystickInfo::POV_HAT_UNKNOWN:
			default:
				std::cout << "unknown" << std::endl;
				break;
			}
		}
	}
	else
	{
		std::cout << "Joystick support is not enabled." << std::endl;
	}

	core::stringw tmp = L"Irrlicht Joystick Example (";
	tmp += joystickInfo.size();
	tmp += " joysticks)";
	device->setWindowCaption(tmp.c_str());

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

	\endcode

	We'll create an arrow mesh and move it around either with the joystick axis/hat,
	or make it follow the mouse pointer. \code
	scene::ISceneNode * node = smgr->addMeshSceneNode(
		smgr->addArrowMesh( "Arrow",
				video::SColor(255, 255, 0, 0),
				video::SColor(255, 0, 255, 0),
				16,16,
				2.f, 1.3f,
				0.1f, 0.6f
				)
		);
	node->setMaterialFlag(video::EMF_LIGHTING, false);

	scene::ICameraSceneNode * camera = smgr->addCameraSceneNode();
	camera->setPosition(core::vector3df(0, 0, -10));

	// As in example 04, we'll use framerate independent movement.
	u32 then = device->getTimer()->getTime();
	const f32 MOVEMENT_SPEED = 5.f;

	while(device->run())
	{
		// Work out a frame delta time.
		const u32 now = device->getTimer()->getTime();
		const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
		then = now;

		bool movedWithJoystick = false;
		core::vector3df nodePosition = node->getPosition();

		if(joystickInfo.size() > 0)
		{
			f32 moveHorizontal = 0.f; // Range is -1.f for full left to +1.f for full right
			f32 moveVertical = 0.f; // -1.f for full down to +1.f for full up.

			const SEvent::SJoystickEvent & joystickData = receiver.GetJoystickState();

			// We receive the full analog range of the axes, and so have to implement our
			// own dead zone.  This is an empirical value, since some joysticks have more
			// jitter or creep around the center point than others.  We'll use 5% of the
			// range as the dead zone, but generally you would want to give the user the
			// option to change this.
			const f32 DEAD_ZONE = 0.05f;

			moveHorizontal =
				(f32)joystickData.Axis[SEvent::SJoystickEvent::AXIS_X] / 32767.f;
			if(fabs(moveHorizontal) < DEAD_ZONE)
				moveHorizontal = 0.f;

			moveVertical =
				(f32)joystickData.Axis[SEvent::SJoystickEvent::AXIS_Y] / -32767.f;
			if(fabs(moveVertical) < DEAD_ZONE)
				moveVertical = 0.f;

			// POV hat info is only currently supported on Windows, but the value is
			// guaranteed to be 65535 if it's not supported, so we can check its range.
			const u16 povDegrees = joystickData.POV / 100;
			if(povDegrees < 360)
			{
				if(povDegrees > 0 && povDegrees < 180)
					moveHorizontal = 1.f;
				else if(povDegrees > 180)
					moveHorizontal = -1.f;

				if(povDegrees > 90 && povDegrees < 270)
					moveVertical = -1.f;
				else if(povDegrees > 270 || povDegrees < 90)
					moveVertical = +1.f;
			}

			if(!core::equals(moveHorizontal, 0.f) || !core::equals(moveVertical, 0.f))
			{
				nodePosition.X += MOVEMENT_SPEED * frameDeltaTime * moveHorizontal;
				nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime * moveVertical;
				movedWithJoystick = true;
			}
		}

		// If the arrow node isn't being moved with the joystick, then have it follow the mouse cursor.
		if(!movedWithJoystick)
		{
			// Create a ray through the mouse cursor.
			core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(
				receiver.GetMouseState().Position, camera);

			// And intersect the ray with a plane around the node facing towards the camera.
			core::plane3df plane(nodePosition, core::vector3df(0, 0, -1));
			core::vector3df mousePosition;
			if(plane.getIntersectionWithLine(ray.start, ray.getVector(), mousePosition))
			{
				// We now have a mouse position in 3d space; move towards it.
				core::vector3df toMousePosition(mousePosition - nodePosition);
				const f32 availableMovement = MOVEMENT_SPEED * frameDeltaTime;

				if(toMousePosition.getLength() <= availableMovement)
					nodePosition = mousePosition; // Jump to the final position
				else
					nodePosition += toMousePosition.normalize() * availableMovement; // Move towards it
			}
		}

		node->setPosition(nodePosition);

		// Turn lighting on and off depending on whether the left mouse button is down.
		node->setMaterialFlag(video::EMF_LIGHTING, receiver.GetMouseState().LeftButtonDown);

		driver->beginScene(true, true, video::SColor(255,113,113,133));
		smgr->drawAll(); // draw the 3d scene
		driver->endScene();
	}

	\endcode

	In the end, delete the Irrlicht device.
	\code
	device->drop();

	return 0;
}

\endcode

**/
/** \page example020 Tutorial 20: Managed Lights
 \image html "020shot.jpg"
 \image latex "020shot.jpg"

Written by Colin MacDonald. This tutorial explains the use of the Light Manager
of Irrlicht. It enables the use of more dynamic light sources than the actual
hardware supports. Further applications of the Light Manager, such as per scene
node callbacks, are left out for simplicity of the example.
\code

#include <irrlicht.h>
#include <iostream>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#if defined(_MSC_VER)
#pragma comment(lib, "Irrlicht.lib")
#endif // MSC_VER

\endcode

    Normally, you are limited to 8 dynamic lights per scene: this is a hardware limit.  If you
    want to use more dynamic lights in your scene, then you can register an optional light
    manager that allows you to to turn lights on and off at specific point during rendering.
    You are still limited to 8 lights, but the limit is per scene node.

    This is completely optional: if you do not register a light manager, then a default
    distance-based scheme will be used to prioritise hardware lights based on their distance
    from the active camera.

	NO_MANAGEMENT disables the light manager and shows Irrlicht's default light behaviour.
    The 8 lights nearest to the camera will be turned on, and other lights will be turned off.
    In this example, this produces a funky looking but incoherent light display.

	LIGHTS_NEAREST_NODE shows an implementation that turns on a limited number of lights
    per mesh scene node.  If finds the 3 lights that are nearest to the node being rendered,
    and turns them on, turning all other lights off.  This works, but as it operates on every
    light for every node, it does not scale well with many lights.  The flickering you can see
    in this demo is due to the lights swapping their relative positions from the cubes
    (a deliberate demonstration of the limitations of this technique).

	LIGHTS_IN_ZONE shows a technique for turning on lights based on a 'zone'. Each empty scene
    node is considered to be the parent of a zone.  When nodes are rendered, they turn off all
    lights, then find their parent 'zone' and turn on all lights that are inside that zone, i.e.
	are  descendents of it in the scene graph.  This produces true 'local' lighting for each cube
    in this example.  You could use a similar technique to locally light all meshes in (e.g.)
    a room, without the lights spilling out to other rooms.

	This light manager is also an event receiver; this is purely for simplicity in this example,
    it's neither necessary nor recommended for a real application.
\code
class CMyLightManager : public ILightManager, public IEventReceiver
{
	typedef enum
	{
		NO_MANAGEMENT,
		LIGHTS_NEAREST_NODE,
		LIGHTS_IN_ZONE
	}
	LightManagementMode;

	LightManagementMode Mode;
	LightManagementMode RequestedMode;

	// These data represent the state information that this light manager
	// is interested in.
	ISceneManager * SceneManager;
	core::array<ILightSceneNode*> * SceneLightList;
	E_SCENE_NODE_RENDER_PASS CurrentRenderPass;
	ISceneNode * CurrentSceneNode;

public:
	CMyLightManager(ISceneManager* sceneManager)
		: Mode(NO_MANAGEMENT), RequestedMode(NO_MANAGEMENT),
		SceneManager(sceneManager),  SceneLightList(0),
		CurrentRenderPass(ESNRP_NONE), CurrentSceneNode(0)
	{ }

	virtual ~CMyLightManager(void) { }

	// The input receiver interface, which just switches light management strategy
	bool OnEvent(const SEvent & event)
	{
		bool handled = false;

		if (event.EventType == irr::EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
		{
			handled = true;
			switch(event.KeyInput.Key)
			{
			case irr::KEY_KEY_1:
				RequestedMode = NO_MANAGEMENT;
				break;
			case irr::KEY_KEY_2:
				RequestedMode = LIGHTS_NEAREST_NODE;
				break;
			case irr::KEY_KEY_3:
				RequestedMode = LIGHTS_IN_ZONE;
				break;
			default:
				handled = false;
				break;
			}

			if(NO_MANAGEMENT == RequestedMode)
				SceneManager->setLightManager(0); // Show that it's safe to register the light manager
			else
				SceneManager->setLightManager(this);
		}

		return handled;
	}


	// This is called before the first scene node is rendered.
	virtual void OnPreRender(core::array<ILightSceneNode*> & lightList)
	{
		// Update the mode; changing it here ensures that it's consistent throughout a render
		Mode = RequestedMode;

		// Store the light list. I am free to alter this list until the end of OnPostRender().
		SceneLightList = &lightList;
	}

	// Called after the last scene node is rendered.
	virtual void OnPostRender()
	{
		// Since light management might be switched off in the event handler, we'll turn all
		// lights on to ensure that they are in a consistent state. You wouldn't normally have
		// to do this when using a light manager, since you'd continue to do light management
		// yourself.
		for(u32 i = 0; i < SceneLightList->size(); i++)
			(*SceneLightList)[i]->setVisible(true);
	}

	virtual void OnRenderPassPreRender(E_SCENE_NODE_RENDER_PASS renderPass)
	{
		// I don't have to do anything here except remember which render pass I am in.
		CurrentRenderPass = renderPass;
	}

	virtual void OnRenderPassPostRender(E_SCENE_NODE_RENDER_PASS renderPass)
	{
		// I only want solid nodes to be lit, so after the solid pass, turn all lights off.
		if(ESNRP_SOLID == renderPass)
		{
			for(u32 i = 0; i < SceneLightList->size(); ++i)
				(*SceneLightList)[i]->setVisible(false);
		}
	}

	// This is called before the specified scene node is rendered
	virtual void OnNodePreRender(ISceneNode* node)
	{
		CurrentSceneNode = node;

		// This light manager only considers solid objects, but you are free to manipulate
		// lights during any phase, depending on your requirements.
		if(ESNRP_SOLID != CurrentRenderPass)
			return;

		// And in fact for this example, I only want to consider lighting for cube scene
		// nodes.  You will probably want to deal with lighting for (at least) mesh /
		// animated mesh scene nodes as well.
		if(node->getType() != ESNT_CUBE)
			return;

		if(LIGHTS_NEAREST_NODE == Mode)
		{
			// This is a naive implementation that prioritises every light in the scene
			// by its proximity to the node being rendered.  This produces some flickering
			// when lights orbit closer to a cube than its 'zone' lights.
			const vector3df nodePosition = node->getAbsolutePosition();

			// Sort the light list by prioritising them based on their distance from the node
			// that's about to be rendered.
			array<LightDistanceElement> sortingArray;
			sortingArray.reallocate(SceneLightList->size());

			u32 i;
			for(i = 0; i < SceneLightList->size(); ++i)
			{
				ILightSceneNode* lightNode = (*SceneLightList)[i];
				f64 distance = lightNode->getAbsolutePosition().getDistanceFromSQ(nodePosition);
				sortingArray.push_back(LightDistanceElement(lightNode, distance));
			}

			sortingArray.sort();

			// The list is now sorted by proximity to the node.
			// Turn on the three nearest lights, and turn the others off.
			for(i = 0; i < sortingArray.size(); ++i)
				sortingArray[i].node->setVisible(i < 3);

		}
		else if(LIGHTS_IN_ZONE == Mode)
		{
			// Empty scene nodes are used to represent 'zones'.  For each solid mesh that
			// is being rendered, turn off all lights, then find its 'zone' parent, and turn
			// on all lights that are found under that node in the scene graph.
			// This is a general purpose algorithm that doesn't use any special
			// knowledge of how this particular scene graph is organised.
			for(u32 i = 0; i < SceneLightList->size(); ++i)
			{
				ILightSceneNode* lightNode = (*SceneLightList)[i];
				SLight & lightData = lightNode->getLightData();

				if(ELT_DIRECTIONAL != lightData.Type)
					lightNode->setVisible(false);
			}

			ISceneNode * parentZone = findZone(node);
			if(parentZone)
				turnOnZoneLights(parentZone);
		}
	}

	// Called after the specified scene node is rendered
	virtual void OnNodePostRender(ISceneNode* node)
	{
		// I don't need to do any light management after individual node rendering.
	}

private:

	// Find the empty scene node that is the parent of the specified node
	ISceneNode * findZone(ISceneNode * node)
	{
		if(!node)
			return 0;

		if(node->getType() == ESNT_EMPTY)
			return node;

		return findZone(node->getParent());
	}

	// Turn on all lights that are children (directly or indirectly) of the
	// specified scene node.
	void turnOnZoneLights(ISceneNode * node)
	{
		core::list<ISceneNode*> const & children = node->getChildren();
		for (core::list<ISceneNode*>::ConstIterator child = children.begin();
			child != children.end();
			++child)
		{
			if((*child)->getType() == ESNT_LIGHT)
				static_cast<ILightSceneNode*>(*child)->setVisible(true);
			else // Assume that lights don't have any children that are also lights
				turnOnZoneLights(*child);
		}
	}


	// A utility class to aid in sorting scene nodes into a distance order
	class LightDistanceElement
	{
	public:
		LightDistanceElement() {};

		LightDistanceElement(ILightSceneNode* n, f64 d)
			: node(n), distance(d) { }

		ILightSceneNode* node;
		f64 distance;

		// Lower distance elements are sorted to the start of the array
		bool operator < (const LightDistanceElement& other) const
		{
			return (distance < other.distance);
		}
	};
};


\endcode

\code
int main(int argumentCount, char * argumentValues[])
{
	char driverChoice;

	if(argumentCount > 1)
		driverChoice = argumentValues[1][0];
	else
	{
		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");

		std::cin >> driverChoice;
	}

	video::E_DRIVER_TYPE driverType;
	switch(driverChoice)
	{
		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 0;
	}


	IrrlichtDevice *device = createDevice(driverType, dimension2d<u32>(640, 480), 32,
										false, false, false, 0);
	if(!device)
		return -1;

	f32 const lightRadius = 60.f; // Enough to reach the far side of each 'zone'

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();

	gui::IGUISkin* skin = guienv->getSkin();
	if (skin)
	{
		skin->setColor(EGDC_BUTTON_TEXT, SColor(255, 255, 255, 255));
		gui::IGUIFont* font = guienv->getFont("../../media/fontlucida.png");
		if(font)
			skin->setFont(font);
	}

	guienv->addStaticText(L"1 - No light management", core::rect<s32>(10,10,200,30));
	guienv->addStaticText(L"2 - Closest 3 lights", core::rect<s32>(10,30,200,50));
	guienv->addStaticText(L"3 - Lights in zone", core::rect<s32>(10,50,200,70));

\endcode

Add several "zones".  You could use this technique to light individual rooms, for example.
\code
	for(f32 zoneX = -100.f; zoneX <= 100.f; zoneX += 50.f)
		for(f32 zoneY = -60.f; zoneY <= 60.f; zoneY += 60.f)
		{
			// Start with an empty scene node, which we will use to represent a zone.
			ISceneNode * zoneRoot = smgr->addEmptySceneNode();
			zoneRoot->setPosition(vector3df(zoneX, zoneY, 0));

			// Each zone contains a rotating cube
			IMeshSceneNode * node = smgr->addCubeSceneNode(15, zoneRoot);
			ISceneNodeAnimator * rotation = smgr->createRotationAnimator(vector3df(0.25f, 0.5f, 0.75f));
			node->addAnimator(rotation);
			rotation->drop();

			// And each cube has three lights attached to it.  The lights are attached to billboards so
			// that we can see where they are.  The billboards are attached to the cube, so that the
			// lights are indirect descendents of the same empty scene node as the cube.
			IBillboardSceneNode * billboard = smgr->addBillboardSceneNode(node);
			billboard->setPosition(vector3df(0, -14, 30));
			billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
			billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
			billboard->setMaterialFlag(video::EMF_LIGHTING, false);
			ILightSceneNode * light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), SColorf(1, 0, 0), lightRadius);

			billboard = smgr->addBillboardSceneNode(node);
			billboard->setPosition(vector3df(-21, -14, -21));
			billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
			billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
			billboard->setMaterialFlag(video::EMF_LIGHTING, false);
			light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), SColorf(0, 1, 0), lightRadius);

			billboard = smgr->addBillboardSceneNode(node);
			billboard->setPosition(vector3df(21, -14, -21));
			billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
			billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
			billboard->setMaterialFlag(video::EMF_LIGHTING, false);
			light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), SColorf(0, 0, 1), lightRadius);

			// Each cube also has a smaller cube rotating around it, to show that the cubes are being
			// lit by the lights in their 'zone', not just lights that are their direct children.
			node = smgr->addCubeSceneNode(5, node);
			node->setPosition(vector3df(0, 21, 0));
		}

	smgr->addCameraSceneNode(0, vector3df(0,0,-130), vector3df(0,0,0));

	CMyLightManager * myLightManager = new CMyLightManager(smgr);
	smgr->setLightManager(0); // This is the default: we won't do light management until told to do it.
	device->setEventReceiver(myLightManager);

	int lastFps = -1;

	while(device->run())
	{
		driver->beginScene(true, true, SColor(255,100,101,140));
		smgr->drawAll();
		guienv->drawAll();
		driver->endScene();

		int fps = driver->getFPS();
		if(fps != lastFps)
		{
			lastFps = fps;
			core::stringw str = L"Managed Lights [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;
			device->setWindowCaption(str.c_str());
		}
	}

	myLightManager->drop(); // Drop my implicit reference
	device->drop();
	return 0;
}

\endcode

**/

/** \page example021 Tutorial 21: Quake3 Explorer
 \image html "021shot.jpg"
 \image latex "021shot.jpg"

This Tutorial shows how to load different Quake 3 maps.

Features:
	- Load BSP Archives at Runtime from the menu
	- Load a Map from the menu. Showing with Screenshot
	- Set the VideoDriver at runtime from menu
	- Adjust GammaLevel at runtime
	- Create SceneNodes for the Shaders
	- Load EntityList and create Entity SceneNodes
	- Create Players with Weapons and with Collison Respsone
	- Play music

You can download the Quake III Arena demo ( copyright id software )
at the following location:
ftp://ftp.idsoftware.com/idstuff/quake3/win32/q3ademo.exe

Copyright 2006-2009 Burningwater, Thomas Alten
\code

#include "q3factory.h"
#include "sound.h"
#include <iostream>

\endcode

	Game Data is used to hold Data which is needed to drive the game
\code
struct GameData
{
	GameData ( const path &startupDir) :
		retVal(0), StartupDir(startupDir), createExDevice(0), Device(0)
	{
		setDefault ();
	}

	void setDefault ();
	s32 save ( const path &filename );
	s32 load ( const path &filename );

	s32 debugState;
	s32 gravityState;
	s32 flyTroughState;
	s32 wireFrame;
	s32 guiActive;
	s32 guiInputActive;
	f32 GammaValue;
	s32 retVal;
	s32 sound;

	path StartupDir;
	stringw CurrentMapName;
	array<path> CurrentArchiveList;

	vector3df PlayerPosition;
	vector3df PlayerRotation;

	tQ3EntityList Variable;

	Q3LevelLoadParameter loadParam;
	SIrrlichtCreationParameters deviceParam;
	funcptr_createDeviceEx createExDevice;
	IrrlichtDevice *Device;
};

\endcode

	set default settings
\code
void GameData::setDefault ()
{
	debugState = EDS_OFF;
	gravityState = 1;
	flyTroughState = 0;
	wireFrame = 0;
	guiActive = 1;
	guiInputActive = 0;
	GammaValue = 1.f;

	// default deviceParam;
#if defined ( _IRR_WINDOWS_ )
	deviceParam.DriverType = EDT_DIRECT3D9;
#else
	deviceParam.DriverType = EDT_OPENGL;
#endif
	deviceParam.WindowSize.Width = 800;
	deviceParam.WindowSize.Height = 600;
	deviceParam.Fullscreen = false;
	deviceParam.Bits = 32;
	deviceParam.ZBufferBits = 16;
	deviceParam.Vsync = false;
	deviceParam.AntiAlias = false;

	// default Quake3 loadParam
	loadParam.defaultLightMapMaterial = EMT_LIGHTMAP;
	loadParam.defaultModulate = EMFN_MODULATE_1X;
	loadParam.defaultFilter = EMF_ANISOTROPIC_FILTER;
	loadParam.verbose = 2;
	loadParam.mergeShaderBuffer = 1;		// merge meshbuffers with same material
	loadParam.cleanUnResolvedMeshes = 1;	// should unresolved meshes be cleaned. otherwise blue texture
	loadParam.loadAllShaders = 1;			// load all scripts in the script directory
	loadParam.loadSkyShader = 0;			// load sky Shader
	loadParam.alpharef = 1;

	sound = 0;

	CurrentMapName = "";
	CurrentArchiveList.clear ();

	// Explorer Media directory
	CurrentArchiveList.push_back ( StartupDir + "../../media/" );

	// Add the original quake3 files before you load your custom map
	// Most mods are using the original shaders, models&items&weapons
	CurrentArchiveList.push_back("/q/baseq3/");

	CurrentArchiveList.push_back(StartupDir + "../../media/map-20kdm2.pk3");
}

\endcode

	Load the current game State from a typical quake3 cfg file
\code
s32 GameData::load ( const path &filename )
{
	if (!Device)
		return 0;

	// the quake3 mesh loader can also handle *.shader and *.cfg file
	IQ3LevelMesh* mesh = (IQ3LevelMesh*) Device->getSceneManager()->getMesh ( filename );
	if (!mesh)
		return 0;

	tQ3EntityList &entityList = mesh->getEntityList ();

	stringc s;
	u32 pos;

	for ( u32 e = 0; e != entityList.size (); ++e )
	{
		//dumpShader ( s, &entityList[e], false );
		//printf ( s.c_str () );

		for ( u32 g = 0; g != entityList[e].getGroupSize (); ++g )
		{
			const SVarGroup *group = entityList[e].getGroup ( g );

			for ( u32 index = 0; index < group->Variable.size (); ++index )
			{
				const SVariable &v = group->Variable[index];
				pos = 0;
				if ( v.name == "playerposition" )
				{
					PlayerPosition = getAsVector3df ( v.content, pos );
				}
				else
				if ( v.name == "playerrotation" )
				{
					PlayerRotation = getAsVector3df ( v.content, pos );
				}
			}
		}
	}

	return 1;
}

\endcode

	Store the current game State in a quake3 configuration file
\code
s32 GameData::save ( const path &filename )
{
	return 0;
	if (!Device)
		return 0;

	c8 buf[128];
	u32 i;

	// Store current Archive for restart
	CurrentArchiveList.clear();
	IFileSystem *fs = Device->getFileSystem();
	for ( i = 0; i != fs->getFileArchiveCount(); ++i )
	{
		CurrentArchiveList.push_back ( fs->getFileArchive(i)->getFileList()->getPath() );
	}

	// Store Player Position and Rotation
	ICameraSceneNode * camera = Device->getSceneManager()->getActiveCamera ();
	if ( camera )
	{
		PlayerPosition = camera->getPosition ();
		PlayerRotation = camera->getRotation ();
	}

	IWriteFile *file = fs->createAndWriteFile ( filename );
	if (!file)
		return 0;

	snprintf ( buf, 128, "playerposition %.f %.f %.f\nplayerrotation %.f %.f %.f\n",
			PlayerPosition.X, PlayerPosition.Z, PlayerPosition.Y,
			PlayerRotation.X, PlayerRotation.Z, PlayerRotation.Y);
	file->write ( buf, (s32) strlen ( buf ) );
	for ( i = 0; i != fs->getFileArchiveCount(); ++i )
	{
		snprintf ( buf, 128, "archive %s\n",stringc ( fs->getFileArchive(i)->getFileList()->getPath() ).c_str () );
		file->write ( buf, (s32) strlen ( buf ) );
	}

	file->drop ();
	return 1;
}

\endcode

	Representing a player
\code
struct Q3Player : public IAnimationEndCallBack
{
	Q3Player ()
	: Device(0), MapParent(0), Mesh(0), WeaponNode(0), StartPositionCurrent(0)
	{
		animation[0] = 0;
		memset(Anim, 0, sizeof(TimeFire)*4);
	}

	virtual void OnAnimationEnd(IAnimatedMeshSceneNode* node);

	void create (	IrrlichtDevice *device,
					IQ3LevelMesh* mesh,
					ISceneNode *mapNode,
					IMetaTriangleSelector *meta
				);
	void shutdown ();
	void setAnim ( const c8 *name );
	void respawn ();
	void setpos ( const vector3df &pos, const vector3df& rotation );

	ISceneNodeAnimatorCollisionResponse * cam() { return camCollisionResponse ( Device ); }

	IrrlichtDevice *Device;
	ISceneNode* MapParent;
	IQ3LevelMesh* Mesh;
	IAnimatedMeshSceneNode* WeaponNode;
	s32 StartPositionCurrent;
	TimeFire Anim[4];
	c8 animation[64];
	c8 buf[64];
};


\endcode  End player
\code
void Q3Player::shutdown ()
{
	setAnim ( 0 );

	dropElement (WeaponNode);

	if ( Device )
	{
		ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();
		dropElement ( camera );
		Device = 0;
	}

	MapParent = 0;
	Mesh = 0;
}


\endcode  create a new player
\code
void Q3Player::create ( IrrlichtDevice *device, IQ3LevelMesh* mesh, ISceneNode *mapNode, IMetaTriangleSelector *meta )
{
	setTimeFire ( Anim + 0, 200, FIRED );
	setTimeFire ( Anim + 1, 5000 );

	if (!device)
		return;
	// load FPS weapon to Camera
	Device = device;
	Mesh = mesh;
	MapParent = mapNode;

	ISceneManager *smgr = device->getSceneManager ();
	IVideoDriver * driver = device->getVideoDriver();

	ICameraSceneNode* camera = 0;

	SKeyMap keyMap[10];
	keyMap[0].Action = EKA_MOVE_FORWARD;
	keyMap[0].KeyCode = KEY_UP;
	keyMap[1].Action = EKA_MOVE_FORWARD;
	keyMap[1].KeyCode = KEY_KEY_W;

	keyMap[2].Action = EKA_MOVE_BACKWARD;
	keyMap[2].KeyCode = KEY_DOWN;
	keyMap[3].Action = EKA_MOVE_BACKWARD;
	keyMap[3].KeyCode = KEY_KEY_S;

	keyMap[4].Action = EKA_STRAFE_LEFT;
	keyMap[4].KeyCode = KEY_LEFT;
	keyMap[5].Action = EKA_STRAFE_LEFT;
	keyMap[5].KeyCode = KEY_KEY_A;

	keyMap[6].Action = EKA_STRAFE_RIGHT;
	keyMap[6].KeyCode = KEY_RIGHT;
	keyMap[7].Action = EKA_STRAFE_RIGHT;
	keyMap[7].KeyCode = KEY_KEY_D;

	keyMap[8].Action = EKA_JUMP_UP;
	keyMap[8].KeyCode = KEY_KEY_J;

	keyMap[9].Action = EKA_CROUCH;
	keyMap[9].KeyCode = KEY_KEY_C;

	camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 0.6f, -1, keyMap, 10, false, 0.6f);
	camera->setName ( "First Person Camera" );
	//camera->setFOV ( 100.f * core::DEGTORAD );
	camera->setFarValue( 20000.f );

	IAnimatedMeshMD2* weaponMesh = (IAnimatedMeshMD2*) smgr->getMesh("gun.md2");
	if ( 0 == weaponMesh )
		return;

	if ( weaponMesh->getMeshType() == EAMT_MD2 )
	{
		s32 count = weaponMesh->getAnimationCount();
		for ( s32 i = 0; i != count; ++i )
		{
			snprintf ( buf, 64, "Animation: %s", weaponMesh->getAnimationName(i) );
			device->getLogger()->log(buf, ELL_INFORMATION);
		}
	}

	WeaponNode = smgr->addAnimatedMeshSceneNode(
						weaponMesh,
						smgr->getActiveCamera(),
						10,
						vector3df( 0, 0, 0),
						vector3df(-90,-90,90)
						);
	WeaponNode->setMaterialFlag(EMF_LIGHTING, false);
	WeaponNode->setMaterialTexture(0, driver->getTexture( "gun.jpg"));
	WeaponNode->setLoopMode ( false );
	WeaponNode->setName ( "tommi the gun man" );

	//create a collision auto response animator
	ISceneNodeAnimator* anim =
		smgr->createCollisionResponseAnimator( meta, camera,
			vector3df(30,45,30),
			getGravity ( "earth" ),
			vector3df(0,40,0),
			0.0005f
		);

	camera->addAnimator( anim );
	anim->drop();

	if ( meta )
	{
		meta->drop ();
	}

	respawn ();
	setAnim ( "idle" );
}


\endcode

	so we need a good starting Position in the level.
	we can ask the Quake3 Loader for all entities with class_name "info_player_deathmatch"
\code
void Q3Player::respawn ()
{
	if (!Device)
		return;
	ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();

	Device->getLogger()->log( "respawn" );

	if ( StartPositionCurrent >= Q3StartPosition (
			Mesh, camera,StartPositionCurrent++,
			cam ()->getEllipsoidTranslation() )
		)
	{
		StartPositionCurrent = 0;
	}
}

\endcode

	set Player position from saved coordinates
\code
void Q3Player::setpos ( const vector3df &pos, const vector3df &rotation )
{
	if (!Device)
		return;
	Device->getLogger()->log( "setpos" );

	ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();
	if ( camera )
	{
		camera->setPosition ( pos );
		camera->setRotation ( rotation );
		//! New. FPSCamera and animators catches reset on animate 0
		camera->OnAnimate ( 0 );
	}
}

\endcode  set the Animation of the player and weapon
\code
void Q3Player::setAnim ( const c8 *name )
{
	if ( name )
	{
		snprintf ( animation, 64, "%s", name );
		if ( WeaponNode )
		{
			WeaponNode->setAnimationEndCallback ( this );
			WeaponNode->setMD2Animation ( animation );
		}
	}
	else
	{
		animation[0] = 0;
		if ( WeaponNode )
		{
			WeaponNode->setAnimationEndCallback ( 0 );
		}
	}
}


// Callback
void Q3Player::OnAnimationEnd(IAnimatedMeshSceneNode* node)
{
	setAnim ( 0 );
}



\endcode  GUI Elements
\code
struct GUI
{
	GUI ()
	{
		memset ( this, 0, sizeof ( *this ) );
	}

	void drop()
	{
		dropElement ( Window );
		dropElement ( Logo );
	}

	IGUIComboBox* VideoDriver;
	IGUIComboBox* VideoMode;
	IGUICheckBox* FullScreen;
	IGUICheckBox* Bit32;
	IGUIScrollBar* MultiSample;
	IGUIButton* SetVideoMode;

	IGUIScrollBar* Tesselation;
	IGUIScrollBar* Gamma;
	IGUICheckBox* Collision;
	IGUICheckBox* Visible_Map;
	IGUICheckBox* Visible_Shader;
	IGUICheckBox* Visible_Fog;
	IGUICheckBox* Visible_Unresolved;
	IGUICheckBox* Visible_Skydome;
	IGUIButton* Respawn;

	IGUITable* ArchiveList;
	IGUIButton* ArchiveAdd;
	IGUIButton* ArchiveRemove;
	IGUIFileOpenDialog* ArchiveFileOpen;
	IGUIButton* ArchiveUp;
	IGUIButton* ArchiveDown;

	IGUIListBox* MapList;
	IGUITreeView* SceneTree;
	IGUIStaticText* StatusLine;
	IGUIImage* Logo;
	IGUIWindow* Window;
};


\endcode

	CQuake3EventHandler controls the game
\code
class CQuake3EventHandler : public IEventReceiver
{
public:

	CQuake3EventHandler( GameData *gameData );
	virtual ~CQuake3EventHandler ();

	void Animate();
	void Render();

	void AddArchive ( const path& archiveName );
	void LoadMap ( const stringw& mapName, s32 collision );
	void CreatePlayers();
	void AddSky( u32 dome, const c8 *texture );
	Q3Player *GetPlayer ( u32 index ) { return &Player[index]; }

	void CreateGUI();
	void SetGUIActive( s32 command);

	bool OnEvent(const SEvent& eve);


private:

	GameData *Game;

	IQ3LevelMesh* Mesh;
	ISceneNode* MapParent;
	ISceneNode* ShaderParent;
	ISceneNode* ItemParent;
	ISceneNode* UnresolvedParent;
	ISceneNode* BulletParent;
	ISceneNode* FogParent;
	ISceneNode * SkyNode;
	IMetaTriangleSelector *Meta;

	c8 buf[256];

	Q3Player Player[2];

	struct SParticleImpact
	{
		u32 when;
		vector3df pos;
		vector3df outVector;
	};
	array<SParticleImpact> Impacts;
	void useItem( Q3Player * player);
	void createParticleImpacts( u32 now );

	void createTextures ();
	void addSceneTreeItem( ISceneNode * parent, IGUITreeViewNode* nodeParent);

	GUI gui;
	void dropMap ();
};

\endcode  Constructor
\code
CQuake3EventHandler::CQuake3EventHandler( GameData *game )
: Game(game), Mesh(0), MapParent(0), ShaderParent(0), ItemParent(0), UnresolvedParent(0),
	BulletParent(0), FogParent(0), SkyNode(0), Meta(0)
{
	buf[0]=0;
	// Also use 16 Bit Textures for 16 Bit RenderDevice
	if ( Game->deviceParam.Bits == 16 )
	{
		game->Device->getVideoDriver()->setTextureCreationFlag(ETCF_ALWAYS_16_BIT, true);
	}

	// Quake3 Shader controls Z-Writing
	game->Device->getSceneManager()->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);

	// create internal textures
	createTextures ();

	sound_init ( game->Device );

	Game->Device->setEventReceiver ( this );
}


// destructor
CQuake3EventHandler::~CQuake3EventHandler ()
{
	Player[0].shutdown ();
	sound_shutdown ();

	Game->save( "explorer.cfg" );

	Game->Device->drop();
}


// create runtime textures smog, fog
void CQuake3EventHandler::createTextures ()
{
	IVideoDriver * driver = Game->Device->getVideoDriver();

	dimension2du dim ( 64, 64 );

	video::ITexture* texture;
	video::IImage* image;
	u32 i;
	u32 x;
	u32 y;
	u32 * data;
	for ( i = 0; i != 8; ++i )
	{
		image = driver->createImage ( video::ECF_A8R8G8B8, dim);
		data = (u32*) image->lock ();
		for ( y = 0; y != dim.Height; ++y )
		{
			for ( x = 0; x != dim.Width; ++x )
			{
				data [x] = 0xFFFFFFFF;
			}
			data = (u32*) ( (u8*) data + image->getPitch() );
		}
		image->unlock();
		snprintf ( buf, 64, "smoke_%02d", i );
		texture = driver->addTexture( buf, image );
		image->drop ();
	}

	// fog
	for ( i = 0; i != 1; ++i )
	{
		image = driver->createImage ( video::ECF_A8R8G8B8, dim);
		data = (u32*) image->lock ();
		for ( y = 0; y != dim.Height; ++y )
		{
			for ( x = 0; x != dim.Width; ++x )
			{
				data [x] = 0xFFFFFFFF;
			}
			data = (u32*) ( (u8*) data + image->getPitch() );
		}
		image->unlock();
		snprintf ( buf, 64, "fog_%02d", i );
		texture = driver->addTexture( buf, image );
		image->drop ();
	}

}


\endcode

	create the GUI
\code
void CQuake3EventHandler::CreateGUI()
{

	IGUIEnvironment *env = Game->Device->getGUIEnvironment();
	IVideoDriver * driver = Game->Device->getVideoDriver();

	gui.drop();

	// set skin font
	IGUIFont* font = env->getFont("fontlucida.png");
	if (font)
		env->getSkin()->setFont(font);
	env->getSkin()->setColor ( EGDC_BUTTON_TEXT, video::SColor(240,0xAA,0xAA,0xAA) );
	env->getSkin()->setColor ( EGDC_3D_HIGH_LIGHT, video::SColor(240,0x22,0x22,0x22) );
	env->getSkin()->setColor ( EGDC_3D_FACE, video::SColor(240,0x44,0x44,0x44) );
	env->getSkin()->setColor ( EGDC_WINDOW, video::SColor(240,0x66,0x66,0x66) );

	// minimal gui size 800x600
	dimension2d<u32> dim ( 800, 600 );
	dimension2d<u32> vdim ( Game->Device->getVideoDriver()->getScreenSize() );

	if ( vdim.Height >= dim.Height && vdim.Width >= dim.Width )
	{
		//dim = vdim;
	}
	else
	{
	}

	gui.Window = env->addWindow ( rect<s32> ( 0, 0, dim.Width, dim.Height ), false, L"Quake3 Explorer" );
	gui.Window->setToolTipText ( L"Quake3Explorer. Loads and show various BSP File Format and Shaders." );
	gui.Window->getCloseButton()->setToolTipText ( L"Quit Quake3 Explorer" );

	// add a status line help text
	gui.StatusLine = env->addStaticText( 0, rect<s32>( 5,dim.Height - 30,dim.Width - 5,dim.Height - 10),
								false, false, gui.Window, -1, true
							);


	env->addStaticText ( L"VideoDriver:", rect<s32>( dim.Width - 400, 24, dim.Width - 310, 40 ),false, false, gui.Window, -1, false );
	gui.VideoDriver = env->addComboBox(rect<s32>( dim.Width - 300, 24, dim.Width - 10, 40 ),gui.Window);
	gui.VideoDriver->addItem(L"Direct3D 9.0c", EDT_DIRECT3D9 );
	gui.VideoDriver->addItem(L"Direct3D 8.1", EDT_DIRECT3D8 );
	gui.VideoDriver->addItem(L"OpenGL 1.5", EDT_OPENGL);
	gui.VideoDriver->addItem(L"Software Renderer", EDT_SOFTWARE);
	gui.VideoDriver->addItem(L"Burning's Video (TM) Thomas Alten", EDT_BURNINGSVIDEO);
	gui.VideoDriver->setSelected ( gui.VideoDriver->getIndexForItemData ( Game->deviceParam.DriverType ) );
	gui.VideoDriver->setToolTipText ( L"Use a VideoDriver" );

	env->addStaticText ( L"VideoMode:", rect<s32>( dim.Width - 400, 44, dim.Width - 310, 60 ),false, false, gui.Window, -1, false );
	gui.VideoMode = env->addComboBox(rect<s32>( dim.Width - 300, 44, dim.Width - 10, 60 ),gui.Window);
	gui.VideoMode->setToolTipText ( L"Supported Screenmodes" );
	IVideoModeList *modeList = Game->Device->getVideoModeList();
	if ( modeList )
	{
		s32 i;
		for ( i = 0; i != modeList->getVideoModeCount (); ++i )
		{
			u16 d = modeList->getVideoModeDepth ( i );
			if ( d < 16 )
				continue;

			u16 w = modeList->getVideoModeResolution ( i ).Width;
			u16 h = modeList->getVideoModeResolution ( i ).Height;
			u32 val = w << 16 | h;

			if ( gui.VideoMode->getIndexForItemData ( val ) >= 0 )
				continue;

			f32 aspect = (f32) w / (f32) h;
			const c8 *a = "";
			if ( core::equals ( aspect, 1.3333333333f ) ) a = "4:3";
			else if ( core::equals ( aspect, 1.6666666f ) ) a = "15:9 widescreen";
			else if ( core::equals ( aspect, 1.7777777f ) ) a = "16:9 widescreen";
			else if ( core::equals ( aspect, 1.6f ) ) a = "16:10 widescreen";
			else if ( core::equals ( aspect, 2.133333f ) ) a = "20:9 widescreen";

			snprintf ( buf, sizeof ( buf ), "%d x %d, %s",w, h, a );
			gui.VideoMode->addItem ( stringw ( buf ).c_str(), val );
		}
	}
	gui.VideoMode->setSelected ( gui.VideoMode->getIndexForItemData (
									Game->deviceParam.WindowSize.Width << 16 |
									Game->deviceParam.WindowSize.Height ) );

	gui.FullScreen = env->addCheckBox ( Game->deviceParam.Fullscreen, rect<s32>( dim.Width - 400, 64, dim.Width - 300, 80 ), gui.Window,-1, L"Fullscreen" );
	gui.FullScreen->setToolTipText ( L"Set Fullscreen or Window Mode" );

	gui.Bit32 = env->addCheckBox ( Game->deviceParam.Bits == 32, rect<s32>( dim.Width - 300, 64, dim.Width - 240, 80 ), gui.Window,-1, L"32Bit" );
	gui.Bit32->setToolTipText ( L"Use 16 or 32 Bit" );

	env->addStaticText ( L"MultiSample:", rect<s32>( dim.Width - 235, 64, dim.Width - 150, 80 ),false, false, gui.Window, -1, false );
	gui.MultiSample = env->addScrollBar( true, rect<s32>( dim.Width - 150, 64, dim.Width - 70, 80 ), gui.Window,-1 );
	gui.MultiSample->setMin ( 0 );
	gui.MultiSample->setMax ( 8 );
	gui.MultiSample->setSmallStep ( 1 );
	gui.MultiSample->setLargeStep ( 1 );
	gui.MultiSample->setPos ( Game->deviceParam.AntiAlias );
	gui.MultiSample->setToolTipText ( L"Set the MultiSample (disable, 1x, 2x, 4x, 8x )" );

	gui.SetVideoMode = env->addButton (rect<s32>( dim.Width - 60, 64, dim.Width - 10, 80 ), gui.Window, -1,L"set" );
	gui.SetVideoMode->setToolTipText ( L"Set Video Mode with current values" );

	env->addStaticText ( L"Gamma:", rect<s32>( dim.Width - 400, 104, dim.Width - 310, 120 ),false, false, gui.Window, -1, false );
	gui.Gamma = env->addScrollBar( true, rect<s32>( dim.Width - 300, 104, dim.Width - 10, 120 ), gui.Window,-1 );
	gui.Gamma->setMin ( 50 );
	gui.Gamma->setMax ( 350 );
	gui.Gamma->setSmallStep ( 1 );
	gui.Gamma->setLargeStep ( 10 );
	gui.Gamma->setPos ( core::floor32 ( Game->GammaValue * 100.f ) );
	gui.Gamma->setToolTipText ( L"Adjust Gamma Ramp ( 0.5 - 3.5)" );
	Game->Device->setGammaRamp ( Game->GammaValue, Game->GammaValue, Game->GammaValue, 0.f, 0.f );


	env->addStaticText ( L"Tesselation:", rect<s32>( dim.Width - 400, 124, dim.Width - 310, 140 ),false, false, gui.Window, -1, false );
	gui.Tesselation = env->addScrollBar( true, rect<s32>( dim.Width - 300, 124, dim.Width - 10, 140 ), gui.Window,-1 );
	gui.Tesselation->setMin ( 2 );
	gui.Tesselation->setMax ( 12 );
	gui.Tesselation->setSmallStep ( 1 );
	gui.Tesselation->setLargeStep ( 1 );
	gui.Tesselation->setPos ( Game->loadParam.patchTesselation );
	gui.Tesselation->setToolTipText ( L"How smooth should curved surfaces be rendered" );

	gui.Collision = env->addCheckBox ( true, rect<s32>( dim.Width - 400, 150, dim.Width - 300, 166 ), gui.Window,-1, L"Collision" );
	gui.Collision->setToolTipText ( L"Set collision on or off ( flythrough ). \nPress F7 on your Keyboard" );
	gui.Visible_Map = env->addCheckBox ( true, rect<s32>( dim.Width - 300, 150, dim.Width - 240, 166 ), gui.Window,-1, L"Map" );
	gui.Visible_Map->setToolTipText ( L"Show or not show the static part the Level. \nPress F3 on your Keyboard" );
	gui.Visible_Shader = env->addCheckBox ( true, rect<s32>( dim.Width - 240, 150, dim.Width - 170, 166 ), gui.Window,-1, L"Shader" );
	gui.Visible_Shader->setToolTipText ( L"Show or not show the Shader Nodes. \nPress F4 on your Keyboard" );
	gui.Visible_Fog = env->addCheckBox ( true, rect<s32>( dim.Width - 170, 150, dim.Width - 110, 166 ), gui.Window,-1, L"Fog" );
	gui.Visible_Fog->setToolTipText ( L"Show or not show the Fog Nodes. \nPress F5 on your Keyboard" );
	gui.Visible_Unresolved = env->addCheckBox ( true, rect<s32>( dim.Width - 110, 150, dim.Width - 10, 166 ), gui.Window,-1, L"Unresolved" );
	gui.Visible_Unresolved->setToolTipText ( L"Show the or not show the Nodes the Engine can't handle. \nPress F6 on your Keyboard" );
	gui.Visible_Skydome = env->addCheckBox ( true, rect<s32>( dim.Width - 110, 180, dim.Width - 10, 196 ), gui.Window,-1, L"Skydome" );
	gui.Visible_Skydome->setToolTipText ( L"Show the or not show the Skydome." );

	//Respawn = env->addButton ( rect<s32>( dim.Width - 260, 90, dim.Width - 10, 106 ), 0,-1, L"Respawn" );

	env->addStaticText ( L"Archives:", rect<s32>( 5, dim.Height - 530, dim.Width - 600,dim.Height - 514 ),false, false, gui.Window, -1, false );

	gui.ArchiveAdd = env->addButton ( rect<s32>( dim.Width - 725, dim.Height - 530, dim.Width - 665, dim.Height - 514 ), gui.Window,-1, L"add" );
	gui.ArchiveAdd->setToolTipText ( L"Add an archive, usually packed zip-archives (*.pk3) to the Filesystem" );
	gui.ArchiveRemove = env->addButton ( rect<s32>( dim.Width - 660, dim.Height - 530, dim.Width - 600, dim.Height - 514 ), gui.Window,-1, L"del" );
	gui.ArchiveRemove->setToolTipText ( L"Remove the selected archive from the FileSystem." );
	gui.ArchiveUp = env->addButton ( rect<s32>( dim.Width - 575, dim.Height - 530, dim.Width - 515, dim.Height - 514 ), gui.Window,-1, L"up" );
	gui.ArchiveUp->setToolTipText ( L"Arrange Archive Look-up Hirachy. Move the selected Archive up" );
	gui.ArchiveDown = env->addButton ( rect<s32>( dim.Width - 510, dim.Height - 530, dim.Width - 440, dim.Height - 514 ), gui.Window,-1, L"down" );
	gui.ArchiveDown->setToolTipText ( L"Arrange Archive Look-up Hirachy. Move the selected Archive down" );


	gui.ArchiveList = env->addTable ( rect<s32>( 5,dim.Height - 510, dim.Width - 450,dim.Height - 410 ), gui.Window  );
	gui.ArchiveList->addColumn ( L"Type", 0 );
	gui.ArchiveList->addColumn ( L"Real File Path", 1 );
	gui.ArchiveList->setColumnWidth ( 0, 60 );
	gui.ArchiveList->setColumnWidth ( 1, 284 );
	gui.ArchiveList->setToolTipText ( L"Show the attached Archives" );


	env->addStaticText ( L"Maps:", rect<s32>( 5, dim.Height - 400, dim.Width - 450,dim.Height - 380 ),false, false, gui.Window, -1, false );
	gui.MapList = env->addListBox ( rect<s32>( 5,dim.Height - 380, dim.Width - 450,dim.Height - 40  ), gui.Window, -1, true  );
	gui.MapList->setToolTipText ( L"Show the current Maps in all Archives.\n Double-Click the Map to start the level" );


	// create a visible Scene Tree
	env->addStaticText ( L"Scenegraph:", rect<s32>( dim.Width - 400, dim.Height - 400, dim.Width - 5,dim.Height - 380 ),false, false, gui.Window, -1, false );
	gui.SceneTree = env->addTreeView(	rect<s32>( dim.Width - 400, dim.Height - 380, dim.Width - 5, dim.Height - 40 ),
									gui.Window, -1, true, true, false );
	gui.SceneTree->setToolTipText ( L"Show the current Scenegraph" );
	gui.SceneTree->getRoot()->clearChilds();
	addSceneTreeItem ( Game->Device->getSceneManager()->getRootSceneNode(), gui.SceneTree->getRoot() );


	IGUIImageList* imageList = env->createImageList(	driver->getTexture ( "iconlist.png" ),
										dimension2di( 32, 32 ), true );

	if ( imageList )
	{
		gui.SceneTree->setImageList( imageList );
		imageList->drop ();
	}


	// load the engine logo
	gui.Logo = env->addImage( driver->getTexture("irrlichtlogo3.png"), position2d<s32>(5, 16 ), true, 0 );
	gui.Logo->setToolTipText ( L"The great Irrlicht Engine" );

	AddArchive ( "" );
}


\endcode

	Add an Archive to the FileSystems und updates the GUI
\code
void CQuake3EventHandler::AddArchive ( const path& archiveName )
{
	IFileSystem *fs = Game->Device->getFileSystem();
	u32 i;

	if ( archiveName.size () )
	{
		bool exists = false;
		for ( i = 0; i != fs->getFileArchiveCount(); ++i )
		{
			if ( fs->getFileArchive(i)->getFileList()->getPath() == archiveName )
			{
				exists = true;
				break;
			}
		}

		if (!exists)
		{
			fs->addFileArchive(archiveName, true, false);
		}
	}

	// store the current archives in game data
	// show the attached Archive in proper order
	if ( gui.ArchiveList )
	{
		gui.ArchiveList->clearRows();

		for ( i = 0; i != fs->getFileArchiveCount(); ++i )
		{
			IFileArchive * archive = fs->getFileArchive ( i );

			u32 index = gui.ArchiveList->addRow(i);

			core::stringw typeName;
			switch(archive->getType())
			{
			case io::EFAT_ZIP:
				typeName = "ZIP";
				break;
			case io::EFAT_GZIP:
				typeName = "gzip";
				break;
			case io::EFAT_FOLDER:
				typeName = "Mount";
				break;
			case io::EFAT_PAK:
				typeName = "PAK";
				break;
			case io::EFAT_TAR:
				typeName = "TAR";
				break;
			default:
				typeName = "archive";
			}

			gui.ArchiveList->setCellText ( index, 0, typeName );
			gui.ArchiveList->setCellText ( index, 1, archive->getFileList()->getPath() );
		}
	}


	// browse the archives for maps
	if ( gui.MapList )
	{
		gui.MapList->clear();

		IGUISpriteBank *bank = Game->Device->getGUIEnvironment()->getSpriteBank("sprite_q3map");
		if ( 0 == bank )
			bank = Game->Device->getGUIEnvironment()->addEmptySpriteBank("sprite_q3map");

		SGUISprite sprite;
		SGUISpriteFrame frame;
		core::rect<s32> r;

		bank->getSprites().clear();
		bank->getPositions().clear ();
		gui.MapList->setSpriteBank ( bank );

		u32 g = 0;
		core::stringw s;

		// browse the attached file system
		fs->setFileListSystem ( FILESYSTEM_VIRTUAL );
		fs->changeWorkingDirectoryTo ( "/maps/" );
		IFileList *fileList = fs->createFileList ();
		fs->setFileListSystem ( FILESYSTEM_NATIVE );

		for ( i=0; i< fileList->getFileCount(); ++i)
		{
			s = fileList->getFullFileName(i);
			if ( s.find ( ".bsp" ) >= 0 )
			{
				// get level screenshot. reformat texture to 128x128
				path c ( s );
				deletePathFromFilename ( c );
				cutFilenameExtension ( c, c );
				c = path ( "levelshots/" ) + c;

				dimension2du dim ( 128, 128 );
				IVideoDriver * driver = Game->Device->getVideoDriver();
				IImage* image = 0;
				ITexture *tex = 0;
				path filename;

				filename = c + ".jpg";
				if ( fs->existFile ( filename ) )
					image = driver->createImageFromFile( filename );
				if ( 0 == image )
				{
					filename = c + ".tga";
					if ( fs->existFile ( filename ) )
						image = driver->createImageFromFile( filename );
				}

				if ( image )
				{
					IImage* filter = driver->createImage ( video::ECF_R8G8B8, dim );
					image->copyToScalingBoxFilter ( filter, 0 );
					image->drop ();
					image = filter;
				}

				if ( image )
				{
					tex = driver->addTexture ( filename, image );
					image->drop ();
				}


				bank->setTexture ( g, tex );

				r.LowerRightCorner.X = dim.Width;
				r.LowerRightCorner.Y = dim.Height;
				gui.MapList->setItemHeight ( r.LowerRightCorner.Y + 4 );
				frame.rectNumber = bank->getPositions().size();
				frame.textureNumber = g;

				bank->getPositions().push_back(r);

				sprite.Frames.set_used ( 0 );
				sprite.Frames.push_back(frame);
				sprite.frameTime = 0;
				bank->getSprites().push_back(sprite);

				gui.MapList->addItem ( s.c_str (), g );
				g += 1;
			}
		}
		fileList->drop ();

		gui.MapList->setSelected ( -1 );
		IGUIScrollBar * bar = (IGUIScrollBar*)gui.MapList->getElementFromId( 0 );
		if ( bar )
			bar->setPos ( 0 );

	}

}

\endcode

	clears the Map in Memory
\code
void CQuake3EventHandler::dropMap ()
{
	IVideoDriver * driver = Game->Device->getVideoDriver();

	driver->removeAllHardwareBuffers ();
	driver->removeAllTextures ();

	Player[0].shutdown ();


	dropElement ( ItemParent );
	dropElement ( ShaderParent );
	dropElement ( UnresolvedParent );
	dropElement ( FogParent );
	dropElement ( BulletParent );


	Impacts.clear();

	if ( Meta )
	{
		Meta = 0;
	}

	dropElement ( MapParent );
	dropElement ( SkyNode );

	// clean out meshes, because textures are invalid
	// TODO: better texture handling;-)
	IMeshCache *cache = Game->Device->getSceneManager ()->getMeshCache();
	cache->clear ();
	Mesh = 0;
}

\endcode  Load new map
\code
void CQuake3EventHandler::LoadMap ( const stringw &mapName, s32 collision )
{
	if ( 0 == mapName.size() )
		return;

	dropMap ();

	IFileSystem *fs = Game->Device->getFileSystem();
	ISceneManager *smgr = Game->Device->getSceneManager ();

	IReadFile* file = fs->createMemoryReadFile ( &Game->loadParam, sizeof ( Game->loadParam ),
													L"levelparameter.cfg", false);

	smgr->getMesh( file );
	file->drop ();

	Mesh = (IQ3LevelMesh*) smgr->getMesh(mapName);
	if ( 0 == Mesh )
		return;

	\endcode

		add the geometry mesh to the Scene ( polygon & patches )
		The Geometry mesh is optimised for faster drawing
	\code

	IMesh *geometry = Mesh->getMesh(E_Q3_MESH_GEOMETRY);
	if ( 0 == geometry || geometry->getMeshBufferCount() == 0)
		return;

	Game->CurrentMapName = mapName;

	//create a collision list
	Meta = 0;

	ITriangleSelector * selector = 0;
	if (collision)
		Meta = smgr->createMetaTriangleSelector();

	//IMeshBuffer *b0 = geometry->getMeshBuffer(0);
	//s32 minimalNodes = b0 ? core::s32_max ( 2048, b0->getVertexCount() / 32 ) : 2048;
	s32 minimalNodes = 2048;

	MapParent = smgr->addMeshSceneNode( geometry );
	//MapParent = smgr->addOctreeSceneNode(geometry, 0, -1, minimalNodes);
	MapParent->setName ( mapName );
	if ( Meta )
	{
		selector = smgr->createOctreeTriangleSelector( geometry,MapParent, minimalNodes);
		//selector = smgr->createTriangleSelector ( geometry, MapParent );
		Meta->addTriangleSelector( selector);
		selector->drop ();
	}

	// logical parent for the items
	ItemParent = smgr->addEmptySceneNode();
	if ( ItemParent )
		ItemParent->setName ( "Item Container" );

	ShaderParent = smgr->addEmptySceneNode();
	if ( ShaderParent )
		ShaderParent->setName ( "Shader Container" );

	UnresolvedParent = smgr->addEmptySceneNode();
	if ( UnresolvedParent )
		UnresolvedParent->setName ( "Unresolved Container" );

	FogParent = smgr->addEmptySceneNode();
	if ( FogParent )
		FogParent->setName ( "Fog Container" );

	// logical parent for the bullets
	BulletParent = smgr->addEmptySceneNode();
	if ( BulletParent )
		BulletParent->setName ( "Bullet Container" );


	\endcode

		now construct SceneNodes for each Shader
		The Objects are stored in the quake mesh E_Q3_MESH_ITEMS
		and the Shader ID is stored in the MaterialParameters
		mostly dark looking skulls and moving lava.. or green flashing tubes?
	\code
	Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_ITEMS,ShaderParent, Meta, false );
	Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_FOG,FogParent, 0, false );
	Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_UNRESOLVED,UnresolvedParent, Meta, true );


	\endcode

		Now construct Models from Entity List
	\code
	Q3ModelFactory ( Game->loadParam, Game->Device, Mesh, ItemParent, false );

}

\endcode

	Adds a SceneNode with an icon to the Scene Tree
\code
void CQuake3EventHandler::addSceneTreeItem( ISceneNode * parent, IGUITreeViewNode* nodeParent)
{
	IGUITreeViewNode* node;
	wchar_t msg[128];

	s32 imageIndex;
	list<ISceneNode*>::ConstIterator it = parent->getChildren().begin();
	for (; it != parent->getChildren().end(); ++it)
	{
		switch ( (*it)->getType () )
		{
			case ESNT_Q3SHADER_SCENE_NODE: imageIndex = 0; break;
			case ESNT_CAMERA: imageIndex = 1; break;
			case ESNT_EMPTY: imageIndex = 2; break;
			case ESNT_MESH: imageIndex = 3; break;
			case ESNT_OCTREE: imageIndex = 3; break;
			case ESNT_ANIMATED_MESH: imageIndex = 4; break;
			case ESNT_SKY_BOX: imageIndex = 5; break;
			case ESNT_BILLBOARD: imageIndex = 6; break;
			case ESNT_PARTICLE_SYSTEM: imageIndex = 7; break;
			case ESNT_TEXT: imageIndex = 8; break;
			default:imageIndex = -1; break;
		}

		if ( imageIndex < 0 )
		{
			swprintf ( msg, 128, L"%hs,%hs",
				Game->Device->getSceneManager ()->getSceneNodeTypeName ( (*it)->getType () ),
				(*it)->getName()
				);
		}
		else
		{
			swprintf ( msg, 128, L"%hs",(*it)->getName() );
		}

		node = nodeParent->addChildBack( msg, 0, imageIndex );

		// Add all Animators
		list<ISceneNodeAnimator*>::ConstIterator ait = (*it)->getAnimators().begin();
		for (; ait != (*it)->getAnimators().end(); ++ait)
		{
			imageIndex = -1;
			swprintf ( msg, 128, L"%hs",
				Game->Device->getSceneManager ()->getAnimatorTypeName ( (*ait)->getType () )
				);

			switch ( (*ait)->getType () )
			{
				case ESNAT_FLY_CIRCLE:
				case ESNAT_FLY_STRAIGHT:
				case ESNAT_FOLLOW_SPLINE:
				case ESNAT_ROTATION:
				case ESNAT_TEXTURE:
				case ESNAT_DELETION:
				case ESNAT_COLLISION_RESPONSE:
				case ESNAT_CAMERA_FPS:
				case ESNAT_CAMERA_MAYA:
				default:
					break;
			}
			node->addChildBack( msg, 0, imageIndex );
		}

		addSceneTreeItem ( *it, node );
	}

}


// Adds life!
void CQuake3EventHandler::CreatePlayers()
{
	Player[0].create ( Game->Device, Mesh, MapParent, Meta );
}

// Adds a skydome to the scene
void CQuake3EventHandler::AddSky( u32 dome, const c8 *texture)
{
	ISceneManager *smgr = Game->Device->getSceneManager ();
	IVideoDriver * driver = Game->Device->getVideoDriver();

	bool oldMipMapState = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

	if ( 0 == dome )
	{
		// irrlicht order
		//static const c8*p[] = { "ft", "lf", "bk", "rt", "up", "dn" };
		// quake3 order
		static const c8*p[] = { "ft", "rt", "bk", "lf", "up", "dn" };

		u32 i = 0;
		snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] );
		SkyNode = smgr->addSkyBoxSceneNode( driver->getTexture ( buf ), 0, 0, 0, 0, 0 );

		if (SkyNode)
		{
			for ( i = 0; i < 6; ++i )
			{
				snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] );
				SkyNode->getMaterial(i).setTexture ( 0, driver->getTexture ( buf ) );
			}
		}
	}
	else
	if ( 1 == dome )
	{
		snprintf ( buf, 64, "%s.jpg", texture );
		SkyNode = smgr->addSkyDomeSceneNode(
			driver->getTexture( buf ),
			32,32,
			1.f,
			1.f,
			1000.f,
			0,
			11
			);
	}
	else
	if ( 2 == dome )
	{
		snprintf ( buf, 64, "%s.jpg", texture );
		SkyNode = smgr->addSkyDomeSceneNode(
			driver->getTexture( buf ),
			16,8,
			0.95f,
			2.f,
			1000.f,
			0,
			11
			);
	}

	if (SkyNode)
		SkyNode->setName("Skydome");
	//SkyNode->getMaterial(0).ZBuffer = video::EMDF_DEPTH_LESS_EQUAL;

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

}


// enable GUI elements
void CQuake3EventHandler::SetGUIActive( s32 command)
{
	bool inputState = false;

	ICameraSceneNode * camera = Game->Device->getSceneManager()->getActiveCamera ();

	switch ( command )
	{
		case 0: Game->guiActive = 0; inputState = !Game->guiActive; break;
		case 1: Game->guiActive = 1; inputState = !Game->guiActive;;break;
		case 2: Game->guiActive ^= 1; inputState = !Game->guiActive;break;
		case 3:
			if ( camera )
				inputState = !camera->isInputReceiverEnabled();
			break;
	}

	if ( camera )
	{
		camera->setInputReceiverEnabled ( inputState );
		Game->Device->getCursorControl()->setVisible( !inputState );
	}

	if ( gui.Window )
	{
		gui.Window->setVisible ( Game->guiActive != 0 );
	}

	if ( Game->guiActive &&
			gui.SceneTree && Game->Device->getGUIEnvironment()->getFocus() != gui.SceneTree
		)
	{
		gui.SceneTree->getRoot()->clearChilds();
		addSceneTreeItem ( Game->Device->getSceneManager()->getRootSceneNode(), gui.SceneTree->getRoot() );
	}

	Game->Device->getGUIEnvironment()->setFocus ( Game->guiActive ? gui.Window: 0 );
}



\endcode

	Handle game input
\code
bool CQuake3EventHandler::OnEvent(const SEvent& eve)
{
	if ( eve.EventType == EET_LOG_TEXT_EVENT )
	{
		return false;
	}

	if ( Game->guiActive && eve.EventType == EET_GUI_EVENT )
	{
		if ( eve.GUIEvent.Caller == gui.MapList && eve.GUIEvent.EventType == gui::EGET_LISTBOX_SELECTED_AGAIN )
		{
			s32 selected = gui.MapList->getSelected();
			if ( selected >= 0 )
			{
				stringw loadMap = gui.MapList->getListItem ( selected );
				if ( 0 == MapParent || loadMap != Game->CurrentMapName )
				{
					printf ( "Loading map %ls\n", loadMap.c_str() );
					LoadMap ( loadMap , 1 );
					if ( 0 == Game->loadParam.loadSkyShader )
					{
						AddSky ( 1, "skydome2" );
					}
					CreatePlayers ();
					CreateGUI ();
					SetGUIActive ( 0 );
					return true;
				}
			}
		}
		else
		if ( eve.GUIEvent.Caller == gui.ArchiveRemove && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
		{
			Game->Device->getFileSystem()->removeFileArchive( gui.ArchiveList->getSelected() );
			Game->CurrentMapName = "";
			AddArchive ( "" );
		}
		else
		if ( eve.GUIEvent.Caller == gui.ArchiveAdd && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
		{
			if ( 0 == gui.ArchiveFileOpen )
			{
				Game->Device->getFileSystem()->setFileListSystem ( FILESYSTEM_NATIVE );
				gui.ArchiveFileOpen = Game->Device->getGUIEnvironment()->addFileOpenDialog ( L"Add Game Archive" , false,gui.Window  );
			}
		}
		else
		if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_FILE_SELECTED )
		{
			AddArchive ( gui.ArchiveFileOpen->getFileName() );
			gui.ArchiveFileOpen = 0;
		}
		else
		if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_DIRECTORY_SELECTED )
		{
			AddArchive ( gui.ArchiveFileOpen->getDirectoryName() );
		}
		else
		if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED )
		{
			gui.ArchiveFileOpen = 0;
		}
		else
		if ( ( eve.GUIEvent.Caller == gui.ArchiveUp || eve.GUIEvent.Caller == gui.ArchiveDown ) &&
			eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
		{
			s32 rel = eve.GUIEvent.Caller == gui.ArchiveUp ? -1 : 1;
			if ( Game->Device->getFileSystem()->moveFileArchive ( gui.ArchiveList->getSelected (), rel ) )
			{
				s32 newIndex = core::s32_clamp ( gui.ArchiveList->getSelected() + rel, 0, gui.ArchiveList->getRowCount() - 1 );
				AddArchive ( "" );
				gui.ArchiveList->setSelected ( newIndex );
				Game->CurrentMapName = "";
			}
		}
		else
		if ( eve.GUIEvent.Caller == gui.VideoDriver && eve.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
		{
			Game->deviceParam.DriverType = (E_DRIVER_TYPE) gui.VideoDriver->getItemData ( gui.VideoDriver->getSelected() );
		}
		else
		if ( eve.GUIEvent.Caller == gui.VideoMode && eve.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
		{
			u32 val = gui.VideoMode->getItemData ( gui.VideoMode->getSelected() );
			Game->deviceParam.WindowSize.Width = val >> 16;
			Game->deviceParam.WindowSize.Height = val & 0xFFFF;
		}
		else
		if ( eve.GUIEvent.Caller == gui.FullScreen && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
		{
			Game->deviceParam.Fullscreen = gui.FullScreen->isChecked();
		}
		else
		if ( eve.GUIEvent.Caller == gui.Bit32 && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
		{
			Game->deviceParam.Bits = gui.Bit32->isChecked() ? 32 : 16;
		}
		else
		if ( eve.GUIEvent.Caller == gui.MultiSample && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
		{
			Game->deviceParam.AntiAlias = gui.MultiSample->getPos();
		}
		else
		if ( eve.GUIEvent.Caller == gui.Tesselation && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
		{
			Game->loadParam.patchTesselation = gui.Tesselation->getPos ();
		}
		else
		if ( eve.GUIEvent.Caller == gui.Gamma && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
		{
			Game->GammaValue = gui.Gamma->getPos () * 0.01f;
			Game->Device->setGammaRamp ( Game->GammaValue, Game->GammaValue, Game->GammaValue, 0.f, 0.f );
		}
		else
		if ( eve.GUIEvent.Caller == gui.SetVideoMode && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
		{
			Game->retVal = 2;
			Game->Device->closeDevice();
		}
		else
		if ( eve.GUIEvent.Caller == gui.Window && eve.GUIEvent.EventType == gui::EGET_ELEMENT_CLOSED )
		{
			Game->Device->closeDevice();
		}
		else
		if ( eve.GUIEvent.Caller == gui.Collision && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
		{
			// set fly through active
			Game->flyTroughState ^= 1;
			Player[0].cam()->setAnimateTarget ( Game->flyTroughState == 0 );

			printf ( "collision %d\n", Game->flyTroughState == 0 );
		}
		else
		if ( eve.GUIEvent.Caller == gui.Visible_Map && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
		{
			bool v = gui.Visible_Map->isChecked();

			if ( MapParent )
			{
				printf ( "static node set visible %d\n",v );
				MapParent->setVisible ( v );
			}
		}
		else
		if ( eve.GUIEvent.Caller == gui.Visible_Shader && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
		{
			bool v = gui.Visible_Shader->isChecked();

			if ( ShaderParent )
			{
				printf ( "shader node set visible %d\n",v );
				ShaderParent->setVisible ( v );
			}
		}
		else
		if ( eve.GUIEvent.Caller == gui.Visible_Skydome && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
		{
			if ( SkyNode )
			{
				bool v = !SkyNode->isVisible();
				printf ( "skynode set visible %d\n",v );
				SkyNode->setVisible ( v );
			}
		}
		else
		if ( eve.GUIEvent.Caller == gui.Respawn && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
		{
			Player[0].respawn ();
		}

		return false;
	}

	// fire
	if ((eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_SPACE &&
		eve.KeyInput.PressedDown == false) ||
		(eve.EventType == EET_MOUSE_INPUT_EVENT && eve.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
		)
	{
		ICameraSceneNode * camera = Game->Device->getSceneManager()->getActiveCamera ();
		if ( camera && camera->isInputReceiverEnabled () )
		{
			useItem( Player + 0 );
		}
	}

	// gui active
	if ((eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_F1 &&
		eve.KeyInput.PressedDown == false) ||
		(eve.EventType == EET_MOUSE_INPUT_EVENT && eve.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
		)
	{
		SetGUIActive ( 2 );
	}

	// check if user presses the key
	if ( eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.PressedDown == false)
	{
		// Escape toggles camera Input
		if ( eve.KeyInput.Key == irr::KEY_ESCAPE )
		{
			SetGUIActive ( 3 );
		}
		else
		if (eve.KeyInput.Key == KEY_F11)
		{
			// screenshot are taken without gamma!
			IImage* image = Game->Device->getVideoDriver()->createScreenShot();
			if (image)
			{
				core::vector3df pos;
				core::vector3df rot;
				ICameraSceneNode * cam = Game->Device->getSceneManager()->getActiveCamera ();
				if ( cam )
				{
					pos = cam->getPosition ();
					rot = cam->getRotation ();
				}

				static const c8 *dName[] = { "null", "software", "burning",
					"d3d8", "d3d9", "opengl" };

				snprintf(buf, 256, "%s_%ls_%.0f_%.0f_%.0f_%.0f_%.0f_%.0f.jpg",
						dName[Game->Device->getVideoDriver()->getDriverType()],
						Game->CurrentMapName.c_str(),
						pos.X, pos.Y, pos.Z,
						rot.X, rot.Y, rot.Z
						);
				path filename ( buf );
				filename.replace ( '/', '_' );
				printf ( "screenshot : %s\n", filename.c_str() );
				Game->Device->getVideoDriver()->writeImageToFile(image, filename, 100 );
				image->drop();
			}
		}
		else
		if (eve.KeyInput.Key == KEY_F9)
		{
			s32 value = EDS_OFF;

			Game->debugState = ( Game->debugState + 1 ) & 3;

			switch ( Game->debugState )
			{
				case 1: value = EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_BBOX_ALL; break;
				case 2: value = EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_SKELETON; break;
			}
\endcode

			// set debug map data on/off
			debugState = debugState == EDS_OFF ?
				EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_BBOX_ALL:
				EDS_OFF;
\code
			if ( ItemParent )
			{
				list<ISceneNode*>::ConstIterator it = ItemParent->getChildren().begin();
				for (; it != ItemParent->getChildren().end(); ++it)
				{
					(*it)->setDebugDataVisible ( value );
				}
			}

			if ( ShaderParent )
			{
				list<ISceneNode*>::ConstIterator it = ShaderParent->getChildren().begin();
				for (; it != ShaderParent->getChildren().end(); ++it)
				{
					(*it)->setDebugDataVisible ( value );
				}
			}

			if ( UnresolvedParent )
			{
				list<ISceneNode*>::ConstIterator it = UnresolvedParent->getChildren().begin();
				for (; it != UnresolvedParent->getChildren().end(); ++it)
				{
					(*it)->setDebugDataVisible ( value );
				}
			}

			if ( FogParent )
			{
				list<ISceneNode*>::ConstIterator it = FogParent->getChildren().begin();
				for (; it != FogParent->getChildren().end(); ++it)
				{
					(*it)->setDebugDataVisible ( value );
				}
			}

			if ( SkyNode )
			{
				SkyNode->setDebugDataVisible ( value );
			}

		}
		else
		if (eve.KeyInput.Key == KEY_F8)
		{
			// set gravity on/off
			Game->gravityState ^= 1;
			Player[0].cam()->setGravity ( getGravity ( Game->gravityState ? "earth" : "none" ) );
			printf ( "gravity %s\n", Game->gravityState ? "earth" : "none" );
		}
		else
		if (eve.KeyInput.Key == KEY_F7)
		{
			// set fly through active
			Game->flyTroughState ^= 1;
			Player[0].cam()->setAnimateTarget ( Game->flyTroughState == 0 );
			if ( gui.Collision )
				gui.Collision->setChecked ( Game->flyTroughState == 0 );

			printf ( "collision %d\n", Game->flyTroughState == 0 );
		}
		else
		if (eve.KeyInput.Key == KEY_F2)
		{
			Player[0].respawn ();
		}
		else
		if (eve.KeyInput.Key == KEY_F3)
		{
			if ( MapParent )
			{
				bool v = !MapParent->isVisible ();
				printf ( "static node set visible %d\n",v );
				MapParent->setVisible ( v );
				if ( gui.Visible_Map )
					gui.Visible_Map->setChecked ( v );
			}
		}
		else
		if (eve.KeyInput.Key == KEY_F4)
		{
			if ( ShaderParent )
			{
				bool v = !ShaderParent->isVisible ();
				printf ( "shader node set visible %d\n",v );
				ShaderParent->setVisible ( v );
				if ( gui.Visible_Shader )
					gui.Visible_Shader->setChecked ( v );
			}
		}
		else
		if (eve.KeyInput.Key == KEY_F5)
		{
			if ( FogParent )
			{
				bool v = !FogParent->isVisible ();
				printf ( "fog node set visible %d\n",v );
				FogParent->setVisible ( v );
				if ( gui.Visible_Fog )
					gui.Visible_Fog->setChecked ( v );
			}

		}
		else
		if (eve.KeyInput.Key == KEY_F6)
		{
			if ( UnresolvedParent )
			{
				bool v = !UnresolvedParent->isVisible ();
				printf ( "unresolved node set visible %d\n",v );
				UnresolvedParent->setVisible ( v );
				if ( gui.Visible_Unresolved )
					gui.Visible_Unresolved->setChecked ( v );
			}
		}
	}

	// check if user presses the key C ( for crouch)
	if ( eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_KEY_C )
	{
		// crouch
		ISceneNodeAnimatorCollisionResponse *anim = Player[0].cam ();
		if ( anim && 0 == Game->flyTroughState )
		{
			if ( false == eve.KeyInput.PressedDown )
			{
				// stand up
				anim->setEllipsoidRadius (  vector3df(30,45,30) );
				anim->setEllipsoidTranslation ( vector3df(0,40,0));

			}
			else
			{
				// on your knees
				anim->setEllipsoidRadius (  vector3df(30,20,30) );
				anim->setEllipsoidTranslation ( vector3df(0,20,0));
			}
			return true;
		}
	}
	return false;
}



\endcode

	useItem
\code
void CQuake3EventHandler::useItem( Q3Player * player)
{
	ISceneManager* smgr = Game->Device->getSceneManager();
	ICameraSceneNode* camera = smgr->getActiveCamera();

	if (!camera)
		return;

	SParticleImpact imp;
	imp.when = 0;

	// get line of camera

	vector3df start = camera->getPosition();

	if ( player->WeaponNode )
	{
		start.X += 0.f;
		start.Y += 0.f;
		start.Z += 0.f;
	}

	vector3df end = (camera->getTarget() - start);
	end.normalize();
	start += end*20.0f;

	end = start + (end * camera->getFarValue());

	triangle3df triangle;
	line3d<f32> line(start, end);

	// get intersection point with map
	const scene::ISceneNode* hitNode;
	if (smgr->getSceneCollisionManager()->getCollisionPoint(
		line, Meta, end, triangle,hitNode))
	{
		// collides with wall
		vector3df out = triangle.getNormal();
		out.setLength(0.03f);

		imp.when = 1;
		imp.outVector = out;
		imp.pos = end;

		player->setAnim ( "pow" );
		player->Anim[1].next += player->Anim[1].delta;
	}
	else
	{
		// doesnt collide with wall
		vector3df start = camera->getPosition();
		if ( player->WeaponNode )
		{
			//start.X += 10.f;
			//start.Y += -5.f;
			//start.Z += 1.f;
		}

		vector3df end = (camera->getTarget() - start);
		end.normalize();
		start += end*20.0f;
		end = start + (end * camera->getFarValue());
	}

	// create fire ball
	ISceneNode* node = 0;
	node = smgr->addBillboardSceneNode( BulletParent,dimension2d<f32>(10,10), start);

	node->setMaterialFlag(EMF_LIGHTING, false);
	node->setMaterialTexture(0, Game->Device->getVideoDriver()->getTexture("fireball.bmp"));
	node->setMaterialType(EMT_TRANSPARENT_ADD_COLOR);

	f32 length = (f32)(end - start).getLength();
	const f32 speed = 5.8f;
	u32 time = (u32)(length / speed);

	ISceneNodeAnimator* anim = 0;

	// set flight line

	anim = smgr->createFlyStraightAnimator(start, end, time);
	node->addAnimator(anim);
	anim->drop();

	snprintf ( buf, 64, "bullet: %s on %.1f,%1.f,%1.f",
				imp.when ? "hit" : "nohit", end.X, end.Y, end.Z );
	node->setName ( buf );


	anim = smgr->createDeleteAnimator(time);
	node->addAnimator(anim);
	anim->drop();

	if (imp.when)
	{
		// create impact note
		imp.when = Game->Device->getTimer()->getTime() +
			(time + (s32) ( ( 1.f + Noiser::get() ) * 250.f ));
		Impacts.push_back(imp);
	}

	// play sound
}

// rendered when bullets hit something
void CQuake3EventHandler::createParticleImpacts( u32 now )
{
	ISceneManager* sm = Game->Device->getSceneManager();

	struct smokeLayer
	{
		const c8 * texture;
		f32 scale;
		f32 minparticleSize;
		f32 maxparticleSize;
		f32 boxSize;
		u32 minParticle;
		u32 maxParticle;
		u32 fadeout;
		u32 lifetime;
	};

	smokeLayer smoke[] =
	{
		{ "smoke2.jpg", 0.4f, 1.5f, 18.f, 20.f, 20, 50, 2000, 10000 },
		{ "smoke3.jpg", 0.2f, 1.2f, 15.f, 20.f, 10, 30, 1000, 12000 }
	};


	u32 i;
	u32 g;
	s32 factor = 1;
	for ( g = 0; g != 2; ++g )
	{
		smoke[g].minParticle *= factor;
		smoke[g].maxParticle *= factor;
		smoke[g].lifetime *= factor;
		smoke[g].boxSize *= Noiser::get() * 0.5f;
	}

	for ( i=0; i < Impacts.size(); ++i)
	{
		if (now < Impacts[i].when)
			continue;

		// create smoke particle system
		IParticleSystemSceneNode* pas = 0;

		for ( g = 0; g != 2; ++g )
		{
			pas = sm->addParticleSystemSceneNode(false, BulletParent, -1, Impacts[i].pos);

			snprintf ( buf, 64, "bullet impact smoke at %.1f,%.1f,%1.f",
				Impacts[i].pos.X,Impacts[i].pos.Y,Impacts[i].pos.Z);
			pas->setName ( buf );

			// create a flat smoke
			vector3df direction = Impacts[i].outVector;
			direction *= smoke[g].scale;
			IParticleEmitter* em = pas->createBoxEmitter(
				aabbox3d<f32>(-4.f,0.f,-4.f,20.f,smoke[g].minparticleSize,20.f),
				direction,smoke[g].minParticle, smoke[g].maxParticle,
				video::SColor(0,0,0,0),video::SColor(0,128,128,128),
				250,4000, 60);

			em->setMinStartSize (dimension2d<f32>( smoke[g].minparticleSize, smoke[g].minparticleSize));
			em->setMaxStartSize (dimension2d<f32>( smoke[g].maxparticleSize, smoke[g].maxparticleSize));

			pas->setEmitter(em);
			em->drop();

			// particles get invisible
			IParticleAffector* paf = pas->createFadeOutParticleAffector(
				video::SColor ( 0, 0, 0, 0 ), smoke[g].fadeout);
			pas->addAffector(paf);
			paf->drop();

			// particle system life time
			ISceneNodeAnimator* anim = sm->createDeleteAnimator( smoke[g].lifetime);
			pas->addAnimator(anim);
			anim->drop();

			pas->setMaterialFlag(video::EMF_LIGHTING, false);
			pas->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA );
			pas->setMaterialTexture(0, Game->Device->getVideoDriver()->getTexture( smoke[g].texture ));
		}


		// play impact sound
		#ifdef USE_IRRKLANG
\endcode

		if (irrKlang)
		{
			audio::ISound* sound =
				irrKlang->play3D(impactSound, Impacts[i].pos, false, false, true);

			if (sound)
			{
				// adjust max value a bit to make to sound of an impact louder
				sound->setMinDistance(400);
				sound->drop();
			}
		}
\code
		#endif


		// delete entry
		Impacts.erase(i);
		i--;
	}
}

\endcode

	render
\code
void CQuake3EventHandler::Render()
{
	IVideoDriver * driver = Game->Device->getVideoDriver();
	if ( 0 == driver )
		return;

	driver->beginScene(true, true, SColor(0,0,0,0));
	Game->Device->getSceneManager ()->drawAll();
	Game->Device->getGUIEnvironment()->drawAll();
	driver->endScene();
}

\endcode

	update the generic scene node
\code
void CQuake3EventHandler::Animate()
{
	u32 now = Game->Device->getTimer()->getTime();

	Q3Player * player = Player + 0;

	checkTimeFire ( player->Anim, 4, now );

	// Query Scene Manager attributes
	if ( player->Anim[0].flags & FIRED )
	{
		ISceneManager *smgr = Game->Device->getSceneManager ();
		wchar_t msg[128];
		IVideoDriver * driver = Game->Device->getVideoDriver();

		IAttributes * attr = smgr->getParameters();
		swprintf ( msg, 128,
			L"Q3 %s [%ls], FPS:%03d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)",
			Game->CurrentMapName.c_str(),
			driver->getName(),
			driver->getFPS (),
			(f32) driver->getPrimitiveCountDrawn( 0 ) * ( 1.f / 1000000.f ),
			attr->getAttributeAsInt ( "culled" ),
			attr->getAttributeAsInt ( "calls" ),
			attr->getAttributeAsInt ( "drawn_solid" ),
			attr->getAttributeAsInt ( "drawn_transparent" ),
			attr->getAttributeAsInt ( "drawn_transparent_effect" )
			);
		Game->Device->setWindowCaption( msg );

		swprintf ( msg, 128,
					L"%03d fps, F1 GUI on/off, F2 respawn, F3-F6 toggle Nodes, F7 Collision on/off"
					L", F8 Gravity on/off, Right Mouse Toggle GUI",
					Game->Device->getVideoDriver()->getFPS ()
				);
		if ( gui.StatusLine )
			gui.StatusLine->setText ( msg );
		player->Anim[0].flags &= ~FIRED;
	}

	// idle..
	if ( player->Anim[1].flags & FIRED )
	{
		if ( strcmp ( player->animation, "idle" ) )
			player->setAnim ( "idle" );

		player->Anim[1].flags &= ~FIRED;
	}

	createParticleImpacts ( now );

}


\endcode  The main game states
\code
void runGame ( GameData *game )
{
	if ( game->retVal >= 3 )
		return;

	game->Device = (*game->createExDevice) ( game->deviceParam );
	if ( 0 == game->Device)
	{
		// could not create selected driver.
		game->retVal = 0;
		return;
	}

	// create an event receiver based on current game data
	CQuake3EventHandler *eventHandler = new CQuake3EventHandler( game );

	// load stored config
	game->load ( "explorer.cfg" );

	// add our media directory and archive to the file system
	for ( u32 i = 0; i < game->CurrentArchiveList.size(); ++i )
	{
		eventHandler->AddArchive ( game->CurrentArchiveList[i] );
	}

	// Load a Map or startup to the GUI
	if ( game->CurrentMapName.size () )
	{
		eventHandler->LoadMap ( game->CurrentMapName, 1 );
		if ( 0 == game->loadParam.loadSkyShader )
			eventHandler->AddSky ( 1, "skydome2" );
		eventHandler->CreatePlayers ();
		eventHandler->CreateGUI ();
		eventHandler->SetGUIActive ( 0 );

		// set player to last position on restart
		if ( game->retVal == 2 )
		{
			eventHandler->GetPlayer( 0 )->setpos ( game->PlayerPosition, game->PlayerRotation );
		}
	}
	else
	{
		// start up empty
		eventHandler->AddSky ( 1, "skydome2" );
		eventHandler->CreatePlayers ();
		eventHandler->CreateGUI ();
		eventHandler->SetGUIActive ( 1 );
		background_music ( "IrrlichtTheme.ogg" );
	}


	game->retVal = 3;
	while( game->Device->run() )
	{
		eventHandler->Animate ();
		eventHandler->Render ();
		//if ( !game->Device->isWindowActive() )
			game->Device->yield();
	}

	game->Device->setGammaRamp ( 1.f, 1.f, 1.f, 0.f, 0.f );
	delete eventHandler;
}

#if defined (_IRR_WINDOWS_) && 0
	#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif


\endcode  The main routine, doing all setup
\code
int IRRCALLCONV main(int argc, char* argv[])
{
	path prgname(argv[0]);
	GameData game ( deletePathFromPath ( prgname, 1 ) );

	// dynamically load irrlicht
	const c8 * dllName = argc > 1 ? argv[1] : "irrlicht.dll";
	game.createExDevice = load_createDeviceEx ( dllName );
	if ( 0 == game.createExDevice )
	{
		game.retVal = 3;
		printf ( "Could not load %s.\n", dllName );
		return game.retVal; // could not load dll
	}

	// start without asking for driver
	game.retVal = 1;
	do
	{
		// if driver could not created, ask for another driver
		if ( game.retVal == 0 )
		{
			game.setDefault ();
			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 Video (TM) Thomas Alten\n"\
				" (otherKey) exit\n\n");

			char i = 'a';
			std::cin >> i;

			switch(i)
			{
				case 'a': game.deviceParam.DriverType = EDT_DIRECT3D9;break;
				case 'b': game.deviceParam.DriverType = EDT_DIRECT3D8;break;
				case 'c': game.deviceParam.DriverType = EDT_OPENGL;   break;
				case 'd': game.deviceParam.DriverType = EDT_SOFTWARE; break;
				case 'e': game.deviceParam.DriverType = EDT_BURNINGSVIDEO;break;
				default: game.retVal = 3; break;
			}
		}
		runGame ( &game );
	} while ( game.retVal < 3 );

	return game.retVal;
}

\endcode

**/

/** \page example022 Tutorial 22: Material Viewer
 \image html "022shot.jpg"
 \image latex "022shot.jpg"

This example can be used to play around with material settings and watch the results.
Only the default non-shader materials are used in here.

You have two nodes to make it easier to see which difference your settings will make.
Additionally you have one lightscenenode and you can set the global ambient values.

\code

#include <irrlicht.h>
#include <iostream>

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

\endcode

	Variables within the empty namespace are globals which are restricted to this file.
\code
namespace
{
	const wchar_t* const DriverTypeNames[] =
	{
		L"NULL",
		L"SOFTWARE",
		L"BURNINGSVIDEO",
		L"DIRECT3D8",
		L"DIRECT3D9",
		L"OPENGL",
		0,
	};

	// For the gui id's
	enum EGUI_IDS
	{
		GUI_ID_OPEN_TEXTURE = 1,
		GUI_ID_QUIT,
		GUI_ID_MAX
	};

	// Name used in texture selection to clear the textures on the node
	const core::stringw CLEAR_TEXTURE = L"CLEAR texture";

	// some useful color constants
	const video::SColor SCOL_BLACK     = video::SColor(255, 0,   0,   0);
	const video::SColor SCOL_BLUE      = video::SColor(255, 0,   0,  255);
	const video::SColor SCOL_CYAN      = video::SColor(255, 0,  255, 255);
	const video::SColor SCOL_GRAY      = video::SColor(255, 128,128, 128);
	const video::SColor SCOL_GREEN     = video::SColor(255, 0,  255,  0);
	const video::SColor SCOL_MAGENTA   = video::SColor(255, 255, 0,  255);
	const video::SColor SCOL_RED       = video::SColor(255, 255, 0,   0);
	const video::SColor SCOL_YELLOW    = video::SColor(255, 255, 255, 0);
	const video::SColor SCOL_WHITE     = video::SColor(255, 255, 255, 255);
};	// namespace

\endcode

	Returns a new unique number on each call.
\code
s32 makeUniqueId()
{
	static int unique = GUI_ID_MAX;
	++unique;
	return unique;
}

\endcode

	Find out which vertex-type is needed for the given material type.
\code
video::E_VERTEX_TYPE getVertexTypeForMaterialType(video::E_MATERIAL_TYPE materialType)
{
	using namespace video;

	switch ( materialType )
	{
		case EMT_SOLID:
			return EVT_STANDARD;

		case EMT_SOLID_2_LAYER:
			return EVT_STANDARD;

		case EMT_LIGHTMAP:
		case EMT_LIGHTMAP_ADD:
		case EMT_LIGHTMAP_M2:
		case EMT_LIGHTMAP_M4:
		case EMT_LIGHTMAP_LIGHTING:
		case EMT_LIGHTMAP_LIGHTING_M2:
		case EMT_LIGHTMAP_LIGHTING_M4:
			return EVT_2TCOORDS;

		case EMT_DETAIL_MAP:
			return EVT_2TCOORDS;

		case EMT_SPHERE_MAP:
			return EVT_STANDARD;

		case EMT_REFLECTION_2_LAYER:
			return EVT_2TCOORDS;

		case EMT_TRANSPARENT_ADD_COLOR:
			return EVT_STANDARD;

		case EMT_TRANSPARENT_ALPHA_CHANNEL:
			return EVT_STANDARD;

		case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
			return EVT_STANDARD;

		case EMT_TRANSPARENT_VERTEX_ALPHA:
			return EVT_STANDARD;

		case EMT_TRANSPARENT_REFLECTION_2_LAYER:
			return EVT_2TCOORDS;

		case EMT_NORMAL_MAP_SOLID:
		case EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR:
		case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
		case EMT_PARALLAX_MAP_SOLID:
		case EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR:
		case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
			return EVT_TANGENTS;

		case EMT_ONETEXTURE_BLEND:
			return EVT_STANDARD;

		case EMT_FORCE_32BIT:
			return EVT_STANDARD;
	}
	return EVT_STANDARD;
}

\endcode

	Custom GUI-control to edit colorvalues.
\code
class CColorControl : public gui::IGUIElement
{
public:
	// Constructor
	CColorControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, IGUIElement* parent, s32 id=-1 )
		: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect< s32 >(pos, pos+core::dimension2d<s32>(80, 75)))
		, DirtyFlag(true)
		, ColorStatic(0)
		, EditAlpha(0)
		, EditRed(0)
		, EditGreen(0)
		, EditBlue(0)
	{
		using namespace gui;
		ButtonSetId = makeUniqueId();

		const core::rect< s32 > rectControls(0,0,AbsoluteRect.getWidth(),AbsoluteRect.getHeight() );
		IGUIStaticText * groupElement =	guiEnv->addStaticText (L"", rectControls, true, false, this, -1, false);
		groupElement->setNotClipped(true);

		guiEnv->addStaticText (text, core::rect<s32>(0,0,80,15), false, false, groupElement, -1, false);

		EditAlpha = addEditForNumbers(guiEnv, core::position2d<s32>(0,15), L"a", -1, groupElement );
		EditRed = addEditForNumbers(guiEnv, core::position2d<s32>(0,30), L"r", -1, groupElement );
		EditGreen = addEditForNumbers(guiEnv, core::position2d<s32>(0,45), L"g", -1, groupElement );
		EditBlue = addEditForNumbers(guiEnv, core::position2d<s32>(0,60), L"b", -1, groupElement );

		ColorStatic = guiEnv->addStaticText (L"", core::rect<s32>(60,15,80,75), true, false, groupElement, -1, true);

		guiEnv->addButton (core::rect<s32>(60,35,80,50), groupElement, ButtonSetId, L"set");
		SetEditsFromColor(Color);
	}

	// event receiver
	virtual bool OnEvent(const SEvent &event)
	{
		if ( event.EventType != EET_GUI_EVENT )
			return false;

		if ( event.GUIEvent.Caller->getID() == ButtonSetId && event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
		{
			Color = GetColorFromEdits();
			SetEditsFromColor(Color);
		}

		return false;
	}

	// set the color values
	void setColor(const video::SColor& col)
	{
		DirtyFlag = true;
		Color = col;
		SetEditsFromColor(Color);
	}

	// get the color values
	const video::SColor& getColor() const
	{
		return Color;
	}

	// To reset the dirty flag
	void resetDirty()
	{
		DirtyFlag = false;
	}

	// when the color was changed the dirty flag is set
	bool isDirty() const
	{
		return DirtyFlag;
	};

protected:

	// Add a staticbox for a description + an editbox so users can enter numbers
	gui::IGUIEditBox* addEditForNumbers(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, s32 id, gui::IGUIElement * parent)
	{
		using namespace gui;

		core::rect< s32 > rect(pos, pos+core::dimension2d<s32>(10, 15));
		guiEnv->addStaticText (text, rect, false, false, parent, -1, false);
		rect += core::position2d<s32>( 20, 0 );
		rect.LowerRightCorner.X += 20;
		gui::IGUIEditBox* edit = guiEnv->addEditBox(L"0", rect, true, parent, id);
		return edit;
	}

	// Get the color value from the editfields
	video::SColor GetColorFromEdits()
	{
		video::SColor col;

		u32 alpha=col.getAlpha();
		if ( EditAlpha )
		{
			alpha = (u32)core::strtol10(  core::stringc( EditAlpha->getText() ).c_str(), 0);
			if ( alpha > 255 )
				alpha = 255;
		}
		col.setAlpha(alpha);

		u32 red=col.getRed();
		if ( EditRed )
		{
			red = (u32)core::strtol10( core::stringc( EditRed->getText() ).c_str(), 0);
			if ( red > 255 )
				red = 255;
		}
		col.setRed(red);

		u32 green=col.getGreen();
		if ( EditGreen )
		{
			green = (u32)core::strtol10( core::stringc( EditGreen->getText() ).c_str(), 0);
			if ( green > 255 )
				green = 255;
		}
		col.setGreen(green);

		u32 blue=col.getBlue();
		if ( EditBlue )
		{
			blue = (u32)core::strtol10( core::stringc( EditBlue->getText() ).c_str(), 0);
			if ( blue > 255 )
				blue = 255;
		}
		col.setBlue(blue);

		return col;
	}

	// Fill the editfields with the value for the given color
	void SetEditsFromColor(video::SColor col)
	{
		DirtyFlag = true;
		if ( EditAlpha )
			EditAlpha->setText( core::stringw(col.getAlpha()).c_str() );
		if ( EditRed )
			EditRed->setText( core::stringw(col.getRed()).c_str() );
		if ( EditGreen )
			EditGreen->setText( core::stringw(col.getGreen()).c_str() );
		if ( EditBlue )
			EditBlue->setText( core::stringw(col.getBlue()).c_str() );
		if ( ColorStatic )
			ColorStatic->setBackgroundColor(col);
	}

private:

	bool DirtyFlag;
	video::SColor Color;
	s32 ButtonSetId;
	gui::IGUIStaticText * ColorStatic;
	gui::IGUIEditBox * EditAlpha;
	gui::IGUIEditBox * EditRed;
	gui::IGUIEditBox * EditGreen;
	gui::IGUIEditBox * EditBlue;
};

\endcode

	Custom GUI-control for to edit all colors typically used in materials and lights
\code
class CAllColorsControl : public gui::IGUIElement
{
public:
	// Constructor
	CAllColorsControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description, bool hasEmissive, IGUIElement* parent, s32 id=-1)
		: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(60,250)))
		, ControlAmbientColor(0), ControlDiffuseColor(0), ControlSpecularColor(0), ControlEmissiveColor(0)
	{
		core::rect<s32> rect(0, 0, 60, 15);
		guiEnv->addStaticText (description, rect, false, false, this, -1, false);
		createColorControls(guiEnv, core::position2d<s32>(0, 15), hasEmissive);
	}

	// Destructor
	virtual ~CAllColorsControl()
	{
		ControlAmbientColor->drop();
		ControlDiffuseColor->drop();
		ControlEmissiveColor->drop();
		ControlSpecularColor->drop();
	}

	// Set the color values to those within the material
	void setColorsToMaterialColors(const video::SMaterial & material)
	{
		ControlAmbientColor->setColor(material.AmbientColor);
		ControlDiffuseColor->setColor(material.DiffuseColor);
		ControlEmissiveColor->setColor(material.EmissiveColor);
		ControlSpecularColor->setColor(material.SpecularColor);
	}

	// Update all changed colors in the material
	void updateMaterialColors(video::SMaterial & material)
	{
		if ( ControlAmbientColor->isDirty() )
			material.AmbientColor = ControlAmbientColor->getColor();
		if ( ControlDiffuseColor->isDirty() )
			material.DiffuseColor = ControlDiffuseColor->getColor();
		if ( ControlEmissiveColor->isDirty() )
			material.EmissiveColor = ControlEmissiveColor->getColor();
		if ( ControlSpecularColor->isDirty() )
			material.SpecularColor = ControlSpecularColor->getColor();
	}

	// Set the color values to those from the light data
	void setColorsToLightDataColors(const video::SLight & lightData)
	{
		ControlAmbientColor->setColor(lightData.AmbientColor.toSColor());
		ControlAmbientColor->setColor(lightData.DiffuseColor.toSColor());
		ControlAmbientColor->setColor(lightData.SpecularColor.toSColor());
	}

	// Update all changed colors in the light data
	void updateMaterialColors(video::SLight & lightData)
	{
		if ( ControlAmbientColor->isDirty() )
			lightData.AmbientColor = video::SColorf( ControlAmbientColor->getColor() );
		if ( ControlDiffuseColor->isDirty() )
			lightData.DiffuseColor = video::SColorf( ControlDiffuseColor->getColor() );
		if ( ControlSpecularColor->isDirty() )
			lightData.SpecularColor = video::SColorf(ControlSpecularColor->getColor() );
	}

	// To reset the dirty flags
	void resetDirty()
	{
		ControlAmbientColor->resetDirty();
		ControlDiffuseColor->resetDirty();
		ControlSpecularColor->resetDirty();
		ControlEmissiveColor->resetDirty();
	}

protected:
	void createColorControls(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, bool hasEmissive)
	{
		ControlAmbientColor = new CColorControl( guiEnv, pos, L"ambient", this);
		ControlDiffuseColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 75), L"diffuse", this );
		ControlSpecularColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 150), L"specular", this );
		if ( hasEmissive )
		{
			ControlEmissiveColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 225), L"emissive", this );
		}
	}

private:
	CColorControl*	ControlAmbientColor;
	CColorControl*	ControlDiffuseColor;
	CColorControl*	ControlSpecularColor;
	CColorControl*	ControlEmissiveColor;
};

\endcode

	GUI-Control to offer a selection of available textures.
\code
class CTextureControl : public gui::IGUIElement
{
public:
	CTextureControl(gui::IGUIEnvironment* guiEnv, video::IVideoDriver * driver, const core::position2d<s32> & pos, IGUIElement* parent, s32 id=-1)
	: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(100,15)))
	, DirtyFlag(true), ComboTexture(0)
	{
		core::rect<s32> rectCombo(0, 0, AbsoluteRect.getWidth(),AbsoluteRect.getHeight());
		ComboTexture = guiEnv->addComboBox (rectCombo, this);
		updateTextures(driver);
	}

	virtual bool OnEvent(const SEvent &event)
	{
		if ( event.EventType != EET_GUI_EVENT )
			return false;

		if ( event.GUIEvent.Caller == ComboTexture && event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
		{
			DirtyFlag = true;
		}

		return false;
	}

	// Workaround for a problem with comboboxes.
	// We have to get in front when the combobox wants to get in front or combobox-list might be drawn below other elements.
	virtual bool bringToFront(IGUIElement* element)
	{
		bool result = gui::IGUIElement::bringToFront(element);
		if ( Parent && element == ComboTexture )
			result &= Parent->bringToFront(this);
		return result;
	}

	// return selected texturename (if any, otherwise 0)
	const wchar_t * getSelectedTextureName() const
	{
		s32 selected = ComboTexture->getSelected();
		if ( selected < 0 )
			return 0;
		return ComboTexture->getItem(selected);
	}

	// reset the dirty flag
	void resetDirty()
	{
		DirtyFlag = false;
	}

	// when the texture was changed the dirty flag is set
	bool isDirty() const
	{
		return DirtyFlag;
	};

	// Put the names of all currenlty loaded textures in a combobox
	void updateTextures(video::IVideoDriver * driver)
	{
		s32 oldSelected = ComboTexture->getSelected();
		s32 selectNew = -1;
		const wchar_t * oldTextureName = 0;
		if ( oldSelected >= 0 )
		{
			oldTextureName = ComboTexture->getItem(oldSelected);
		}
		ComboTexture->clear();
		for ( u32 i=0; i < driver->getTextureCount(); ++i )
		{
			video::ITexture * texture = driver->getTextureByIndex(i);
			core::stringw name( texture->getName() );
			ComboTexture->addItem( name.c_str() );
			if ( oldTextureName && selectNew < 0 && name == oldTextureName )
				selectNew = i;
		}

		// add another name which can be used to clear the texture
		ComboTexture->addItem( CLEAR_TEXTURE.c_str() );
		if ( CLEAR_TEXTURE == oldTextureName )
			selectNew = ComboTexture->getItemCount()-1;

		if ( selectNew >= 0 )
			ComboTexture->setSelected(selectNew);

		DirtyFlag = true;
	}

private:
	bool DirtyFlag;
	gui::IGUIComboBox * ComboTexture;
};

\endcode

	Control which allows setting some of the material values for a meshscenenode
\code
struct SMeshNodeControl
{
	// constructor
	SMeshNodeControl()
		: Initialized(false), Driver(0), MeshManipulator(0), SceneNode(0), SceneNode2T(0), SceneNodeTangents(0)
		, AllColorsControl(0), ButtonLighting(0), InfoLighting(0), ComboMaterial(0), TextureControl1(0), TextureControl2(0), ControlVertexColors(0)
	{
	}

	// Destructor
	virtual ~SMeshNodeControl()
	{
		if ( TextureControl1 )
			TextureControl1->drop();
		if ( TextureControl2 )
			TextureControl2->drop();
		if ( ControlVertexColors )
			ControlVertexColors->drop();
	}

	void init(scene::IMeshSceneNode* node, IrrlichtDevice * device, const core::position2d<s32> & pos, const wchar_t * description)
	{
		if ( Initialized || !node || !device) // initializing twice or with invalid data not allowed
			return;

		Driver = device->getVideoDriver ();
		gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();
		scene::ISceneManager* smgr = device->getSceneManager();
		MeshManipulator = smgr->getMeshManipulator();

		SceneNode = node;
		scene::IMeshManipulator * meshManip = smgr->getMeshManipulator();

		scene::IMesh * mesh2T = meshManip->createMeshWith2TCoords(node->getMesh());
		SceneNode2T = smgr->addMeshSceneNode(mesh2T, 0, -1, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
		mesh2T->drop();

		scene::IMesh * meshTangents = meshManip->createMeshWithTangents(node->getMesh(), false, false, false);
		SceneNodeTangents = smgr->addMeshSceneNode(meshTangents, 0, -1
											, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
		meshTangents->drop();

		video::SMaterial & material = SceneNode->getMaterial(0);
		material.Lighting = true;
		AllColorsControl = new CAllColorsControl(guiEnv, pos, description, true, guiEnv->getRootGUIElement());
		AllColorsControl->setColorsToMaterialColors(material);

		core::rect<s32> rectBtn(pos + core::position2d<s32>(0, 320), core::dimension2d<s32>(60, 15));
		ButtonLighting = guiEnv->addButton (rectBtn, 0, -1, L"Lighting");
		ButtonLighting->setIsPushButton(true);
		ButtonLighting->setPressed(material.Lighting);
		core::rect<s32> rectInfo( rectBtn.LowerRightCorner.X, rectBtn.UpperLeftCorner.Y, rectBtn.LowerRightCorner.X+40, rectBtn.UpperLeftCorner.Y+15 );
		InfoLighting = guiEnv->addStaticText(L"", rectInfo, true, false );
		InfoLighting->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER );

		core::rect<s32> rectCombo(pos.X, rectBtn.LowerRightCorner.Y, pos.X+100, rectBtn.LowerRightCorner.Y+15);
		ComboMaterial = guiEnv->addComboBox (rectCombo);
		for ( int i=0; i <= (int)video::EMT_ONETEXTURE_BLEND; ++i )
		{
			ComboMaterial->addItem( core::stringw(video::sBuiltInMaterialTypeNames[i]).c_str() );
		}
		ComboMaterial->setSelected( (s32)material.MaterialType );

		core::position2d<s32> posTex(rectCombo.UpperLeftCorner.X,rectCombo.LowerRightCorner.Y);
		TextureControl1 = new CTextureControl(guiEnv, Driver, posTex, guiEnv->getRootGUIElement());
		posTex.Y += 15;
		TextureControl2 = new CTextureControl(guiEnv, Driver, posTex, guiEnv->getRootGUIElement());

		core::position2d<s32> posVertexColors( posTex.X, posTex.Y + 15);
		ControlVertexColors = new CColorControl( guiEnv, posVertexColors, L"Vertex colors", guiEnv->getRootGUIElement());

		video::S3DVertex * vertices =  (video::S3DVertex *)node->getMesh()->getMeshBuffer(0)->getVertices();
		if ( vertices )
		{
			ControlVertexColors->setColor(vertices[0].Color);
		}

		Initialized = true;
	}

	void update()
	{
		if ( !Initialized )
			return;

		video::SMaterial & material = SceneNode->getMaterial(0);
		video::SMaterial & material2T = SceneNode2T->getMaterial(0);
		video::SMaterial & materialTangents = SceneNodeTangents->getMaterial(0);

		s32 selectedMaterial = ComboMaterial->getSelected();
		if ( selectedMaterial >= (s32)video::EMT_SOLID && selectedMaterial <= (s32)video::EMT_ONETEXTURE_BLEND)
		{
			video::E_VERTEX_TYPE vertexType = getVertexTypeForMaterialType((video::E_MATERIAL_TYPE)selectedMaterial);
			switch ( vertexType )
			{
				case video::EVT_STANDARD:
					material.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
					SceneNode->setVisible(true);
					SceneNode2T->setVisible(false);
					SceneNodeTangents->setVisible(false);
					break;
				case video::EVT_2TCOORDS:
					material2T.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
					SceneNode->setVisible(false);
					SceneNode2T->setVisible(true);
					SceneNodeTangents->setVisible(false);
					break;
				case video::EVT_TANGENTS:
					materialTangents.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
					SceneNode->setVisible(false);
					SceneNode2T->setVisible(false);
					SceneNodeTangents->setVisible(true);
					break;
			}
		}

		updateMaterial(material);
		updateMaterial(material2T);
		updateMaterial(materialTangents);

		if ( ButtonLighting->isPressed() )
			InfoLighting->setText(L"on");
		else
			InfoLighting->setText(L"off");

		AllColorsControl->resetDirty();
		TextureControl1->resetDirty();
		TextureControl2->resetDirty();
		ControlVertexColors->resetDirty();
	}

	void updateTextures()
	{
		TextureControl1->updateTextures(Driver);
		TextureControl2->updateTextures(Driver);
	}

protected:

	void updateMaterial(video::SMaterial & material)
	{
		AllColorsControl->updateMaterialColors(material);
		material.Lighting = ButtonLighting->isPressed();
		if ( TextureControl1->isDirty() )
		{
			material.TextureLayer[0].Texture = Driver->getTexture( io::path(TextureControl1->getSelectedTextureName()) );
		}
		if ( TextureControl2->isDirty() )
		{
			material.TextureLayer[1].Texture = Driver->getTexture( io::path(TextureControl2->getSelectedTextureName()) );
		}
		if ( ControlVertexColors->isDirty() )
		{
			MeshManipulator->setVertexColors (SceneNode->getMesh(), ControlVertexColors->getColor());
			MeshManipulator->setVertexColors (SceneNode2T->getMesh(), ControlVertexColors->getColor());
			MeshManipulator->setVertexColors (SceneNodeTangents->getMesh(), ControlVertexColors->getColor());
		}
	}

	bool Initialized;
	video::IVideoDriver * 		Driver;
	scene::IMeshManipulator* 	MeshManipulator;
	scene::IMeshSceneNode* 		SceneNode;
	scene::IMeshSceneNode* 		SceneNode2T;
	scene::IMeshSceneNode* 		SceneNodeTangents;
	CAllColorsControl* 			AllColorsControl;
	gui::IGUIButton * 			ButtonLighting;
	gui::IGUIStaticText* 		InfoLighting;
	gui::IGUIComboBox * 		ComboMaterial;
	CTextureControl* 			TextureControl1;
	CTextureControl* 			TextureControl2;
	CColorControl*				ControlVertexColors;
};

\endcode

	Control to allow setting the color values of a lightscenenode.
\code
struct SLightNodeControl
{
	// constructor
	SLightNodeControl() : Initialized(false), SceneNode(0), AllColorsControl(0)
	{
	}

	void init(scene::ILightSceneNode* node, gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description)
	{
		if ( Initialized || !node || !guiEnv) // initializing twice or with invalid data not allowed
			return;
		SceneNode = node;
		AllColorsControl = new CAllColorsControl(guiEnv, pos, description, false, guiEnv->getRootGUIElement());
		const video::SLight & lightData = SceneNode->getLightData();
		AllColorsControl->setColorsToLightDataColors(lightData);
		Initialized = true;
	}

	void update()
	{
		if ( !Initialized )
			return;

		video::SLight & lightData = SceneNode->getLightData();
		AllColorsControl->updateMaterialColors(lightData);
	}

protected:
	bool Initialized;
	scene::ILightSceneNode* SceneNode;
	CAllColorsControl* AllColorsControl;
};

\endcode

	Application configuration
\code
struct SConfig
{
	SConfig()
	: RenderInBackground(true)
	, DriverType(video::EDT_BURNINGSVIDEO)
	, ScreenSize(640, 480)
	{
	}

	bool RenderInBackground;
	video::E_DRIVER_TYPE DriverType;
	core::dimension2d<u32> ScreenSize;
};

\endcode

	Main application class
\code
class CApp : public IEventReceiver
{
	friend int main(int argc, char *argv[]);

public:
	// constructor
	CApp()
	: IsRunning(false)
	, Device(0)
	, Camera(0)
	, GlobalAmbient(0)
	{
	}

	// destructor
	~CApp()
	{
	}

	// stop running - will quit at end of mainloop
	void stopApp()
	{
		IsRunning = false;
	}

	// Event handler
	virtual bool OnEvent(const SEvent &event)
	{
		if (event.EventType == EET_GUI_EVENT)
		{
			gui::IGUIEnvironment* env = Device->getGUIEnvironment();

			switch(event.GUIEvent.EventType)
			{
				case gui::EGET_MENU_ITEM_SELECTED:
				{
					gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)event.GUIEvent.Caller;
					s32 id = menu->getItemCommandId(menu->getSelectedItem());

					switch(id)
					{
						case GUI_ID_OPEN_TEXTURE: // File -> Open Texture
							env->addFileOpenDialog(L"Please select a texture file to open");
						break;
						case GUI_ID_QUIT: // File -> Quit
							stopApp();
						break;
					}
				}
				break;

				case gui::EGET_FILE_SELECTED:
				{
					// load the model file, selected in the file open dialog
					gui::IGUIFileOpenDialog* dialog =
						(gui::IGUIFileOpenDialog*)event.GUIEvent.Caller;
					loadTexture(io::path(dialog->getFileName()).c_str());
				}
				break;

				default:
				break;
			}
		}

		return false;
	}

protected:

	// Application initialization
	// returns true when it was succesful initialized, otherwise false.
	bool init(int argc, char *argv[])
	{
		// ask user for the driver which should be used
		Config.DriverType = getDriverTypeFromConsole();
		if ( (int)Config.DriverType < 0 )
			return false;

		// create the device with the settings from our config
		Device = createDevice(Config.DriverType, Config.ScreenSize);
		if (!Device)
			return false;
		Device->setWindowCaption( DriverTypeNames[Config.DriverType] );
		Device->setEventReceiver(this);
		
		scene::ISceneManager* smgr = Device->getSceneManager();
		video::IVideoDriver * driver = Device->getVideoDriver ();
		gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();

		// set a nicer font
		gui::IGUISkin* skin = guiEnv->getSkin();
		gui::IGUIFont* font = guiEnv->getFont("../../media/fonthaettenschweiler.bmp");
		if (font)
			skin->setFont(font);
		
		// remove some alpha value because it makes those menus harder to read otherwise
		video::SColor col3dHighLight( skin->getColor(gui::EGDC_APP_WORKSPACE) );
		col3dHighLight.setAlpha(255);
		video::SColor colHighLight( col3dHighLight );
		skin->setColor(gui::EGDC_HIGH_LIGHT, colHighLight );
		skin->setColor(gui::EGDC_3D_HIGH_LIGHT, col3dHighLight );

		// Add some textures which are useful to test material settings
		createDefaultTextures(driver);

		// create a menu 
		gui::IGUIContextMenu * menuBar = guiEnv->addMenu();
		menuBar->addItem(L"File", -1, true, true);

		gui::IGUIContextMenu* subMenuFile = menuBar->getSubMenu(0);
		subMenuFile->addItem(L"Open texture ...", GUI_ID_OPEN_TEXTURE);
		subMenuFile->addSeparator();
		subMenuFile->addItem(L"Quit", GUI_ID_QUIT);

		// a static camera
		Camera = smgr->addCameraSceneNode (0, core::vector3df(0, 0, 0),
											core::vector3df(0, 0, 100),
											-1);

		// add the nodes which are used to show the materials
		scene::IMeshSceneNode* nodeL = smgr->addCubeSceneNode (30.0f, 0, -1,
										   core::vector3df(-35, 0, 100),
										   core::vector3df(0, 0, 0),
										   core::vector3df(1.0f, 1.0f, 1.0f));
		NodeLeft.init( nodeL, Device, core::position2d<s32>(10,20), L"left node" );

		scene::IMeshSceneNode* nodeR = smgr->addCubeSceneNode (30.0f, 0, -1,
										   core::vector3df(35, 0, 100),
										   core::vector3df(0, 0, 0),
										   core::vector3df(1.0f, 1.0f, 1.0f));
		NodeRight.init( nodeR, Device, core::position2d<s32>(530,20), L"right node" );

		// add one light
		scene::ILightSceneNode* nodeLight = smgr->addLightSceneNode(0, core::vector3df(0, 0, 0),
														video::SColorf(1.0f, 1.0f, 1.0f),
														100.0f);
		LightControl.init(nodeLight, guiEnv, core::position2d<s32>(270,20), L"light" );
		
		// one large cube around everything. That's mainly to make the light more obvious.
		scene::IMeshSceneNode* backgroundCube = smgr->addCubeSceneNode (200.0f, 0, -1, core::vector3df(0, 0, 0),
										   core::vector3df(45, 0, 0),
										   core::vector3df(1.0f, 1.0f, 1.0f));
		backgroundCube->getMaterial(0).BackfaceCulling = false;	 		// we are within the cube, so we have to disable backface culling to see it
		backgroundCube->getMaterial(0).EmissiveColor.set(255,50,50,50);	// we keep some self lighting to keep texts visible

		// set the ambient light value
		GlobalAmbient = new CColorControl( guiEnv, core::position2d<s32>(270, 300), L"global ambient", guiEnv->getRootGUIElement());
		GlobalAmbient->setColor( smgr->getAmbientLight().toSColor() );

		return true;
	}

	// Ask the user which driver to use
	video::E_DRIVER_TYPE getDriverTypeFromConsole()
	{
		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': return video::EDT_DIRECT3D9;
			case 'b': return video::EDT_DIRECT3D8;
			case 'c': return video::EDT_OPENGL;
			case 'd': return video::EDT_SOFTWARE;
			case 'e': return video::EDT_BURNINGSVIDEO;
			case 'f': return video::EDT_NULL;
			default: return video::E_DRIVER_TYPE(-1);
		}
	}

	// Update one frame
	bool update()
	{
		using namespace irr;

		video::IVideoDriver* videoDriver =  Device->getVideoDriver();
		if ( !Device->run() )
			return false;

		if ( Device->isWindowActive() || Config.RenderInBackground )
		{
			gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
			scene::ISceneManager* smgr = Device->getSceneManager();
			gui::IGUISkin * skin = guiEnv->getSkin();

			// update our controls
			NodeLeft.update();
			NodeRight.update();
			LightControl.update();

			// update ambient light settings
			if ( GlobalAmbient->isDirty() )
			{
				smgr->setAmbientLight( GlobalAmbient->getColor() );
				GlobalAmbient->resetDirty();
			}

			// draw everythings
			video::SColor bkColor( skin->getColor(gui::EGDC_APP_WORKSPACE) );
			videoDriver->beginScene(true, true, bkColor);

			smgr->drawAll();
			guiEnv->drawAll();

			videoDriver->endScene();
		}

		return true;
	}

	// Run the application. Our main loop.
	void run()
	{
		IsRunning = true;

		if ( !Device )
			return;

		// main application loop
		while(IsRunning)
		{
			if ( !update() )
				break;

			Device->sleep( 5 );
		}
	}

	// Close down the application
	void quit()
	{
		IsRunning = false;
		GlobalAmbient->drop();
		GlobalAmbient = NULL;
		if ( Device )
		{
			Device->closeDevice();
			Device->drop();
			Device = NULL;
		}
	}

	// Create some useful textures.
	// Note that the function put readability over speed, you shouldn't use setPixel at runtime but for initialization it's nice.
	void createDefaultTextures(video::IVideoDriver * driver)
	{
		const u32 width = 256;
		const u32 height = 256;
		video::IImage * imageA8R8G8B8 = driver->createImage (video::ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
		if ( !imageA8R8G8B8 )
			return;
		const u32 pitch = imageA8R8G8B8->getPitch();

		// some nice caro with 9 typical colors
		for ( u32 y = 0; y < height; ++ y )
		{
			for ( u32 x = 0; x < pitch; ++x )
			{
				if ( y < height/3 )
				{
					if ( x < width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_BLACK);
					else if ( x < 2*width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_BLUE);
					else
						imageA8R8G8B8->setPixel (x, y, SCOL_CYAN);
				}
				else if ( y < 2*height/3 )
				{
					if ( x < width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_GRAY);
					else if ( x < 2*width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_GREEN);
					else
						imageA8R8G8B8->setPixel (x, y, SCOL_MAGENTA);
				}
				else
				{
					if ( x < width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_RED);
					else if ( x < 2*width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_YELLOW);
					else
						imageA8R8G8B8->setPixel (x, y, SCOL_WHITE);
				}
			}
		}
		driver->addTexture (io::path("CARO_A8R8G8B8"), imageA8R8G8B8);

		// all white
		imageA8R8G8B8->fill(SCOL_WHITE);
		driver->addTexture (io::path("WHITE_A8R8G8B8"), imageA8R8G8B8);

		// all black
		imageA8R8G8B8->fill(SCOL_BLACK);
		driver->addTexture (io::path("BLACK_A8R8G8B8"), imageA8R8G8B8);

		// gray-scale
		for ( u32 y = 0; y < height; ++ y )
		{
			for ( u32 x = 0; x < pitch; ++x )
			{
				imageA8R8G8B8->setPixel (x, y, video::SColor(y, x,x,x) );
			}
		}
		driver->addTexture (io::path("GRAYSCALE_A8R8G8B8"), imageA8R8G8B8);
	}

	// Load a texture and make sure nodes know it when more textures are available.
	void loadTexture(const io::path &name)
	{
		Device->getVideoDriver()->getTexture(name);
		NodeLeft.updateTextures();
		NodeRight.updateTextures();
	}

private:
	SConfig						Config;
	volatile bool				IsRunning;
	IrrlichtDevice * 			Device;
	scene::ICameraSceneNode *	Camera;
	SMeshNodeControl			NodeLeft;
	SMeshNodeControl			NodeRight;
	SLightNodeControl			LightControl;
	CColorControl *				GlobalAmbient;
};

\endcode

  A very short main as we do everything else in classes.
\code
int main(int argc, char *argv[])
{
	CApp APP;

	if ( !APP.init(argc, argv) )
	{
		printf("init failed\n");
		return 1;
	}

	APP.run();
	APP.quit();

	return 0;
}

\endcode

**/
/** \page example023 Tutorial 23: SMeshBufferHandling
 \image html "023shot.jpg"
 \image latex "023shot.jpg"

A tutorial by geoff.

In this tutorial we'll learn how to create custom meshes and deal with them
with Irrlicht. We'll create an interesting heightmap with some lighting effects.
With keys 1,2,3 you can choose a different mesh layout, which is put into the
mesh buffers as desired. All positions, normals, etc. are updated accordingly.

Ok, let's start with the headers (I think there's nothing to say about it)
\code

#include <irrlicht.h>
#include <iostream>

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

//Namespaces for the engine
using namespace irr;
using namespace video;
using namespace core;
using namespace scene;
using namespace io;
using namespace gui;

\endcode  This is the type of the functions which work out the colour. \code
typedef SColor colour_func(f32 x, f32 y, f32 z);

\endcode  Here comes a set of functions which can be used for coloring the nodes while
creating the mesh. \code

// Greyscale, based on the height.
SColor grey(f32, f32, f32 z)
{
	u32 n = (u32)(255.f * z);
	return SColor(255, n, n, n);
}

// Interpolation between blue and white, with red added in one
// direction and green in the other.
SColor yellow(f32 x, f32 y, f32)
{
	return SColor(255, 128 + (u32)(127.f * x), 128 + (u32)(127.f * y), 255);
}

// Pure white.
SColor white(f32, f32, f32) { return SColor(255, 255, 255, 255); }

\endcode  The type of the functions which generate the heightmap. x and y
range between -0.5 and 0.5, and s is the scale of the heightmap. \code

typedef f32 generate_func(s16 x, s16 y, f32 s);

// An interesting sample function :-)
f32 eggbox(s16 x, s16 y, f32 s)
{
	const f32 r = 4.f*sqrtf((f32)(x*x + y*y))/s;
	const f32 z = expf(-r * 2) * (cosf(0.2f * x) + cosf(0.2f * y));
	return 0.25f+0.25f*z;
}

// A rather dumb sine function :-/
f32 moresine(s16 x, s16 y, f32 s)
{
	const f32 xx=0.3f*(f32)x/s;
	const f32 yy=12*y/s;
	const f32 z = sinf(xx*xx+yy)*sinf(xx+yy*yy);
	return 0.25f + 0.25f * z;
}

// A simple function
f32 justexp(s16 x, s16 y, f32 s)
{
	const f32 xx=6*x/s;
	const f32 yy=6*y/s;
	const f32 z = (xx*xx+yy*yy);
	return 0.3f*z*cosf(xx*yy);
}

\endcode  A simple class for representing heightmaps. Most of this should be obvious. \code

class HeightMap
{
private:
	const u16 Width;
	const u16 Height;
	f32 s;
	core::array<f32> data;
public:
	HeightMap(u16 _w, u16 _h) : Width(_w), Height(_h), s(0.f), data(0)
	{
		s = sqrtf((f32)(Width * Width + Height * Height));
		data.set_used(Width * Height);
	}

	// Fill the heightmap with values generated from f.
	void generate(generate_func f)
	{
		u32 i=0;
		for(u16 y = 0; y < Height; ++y)
			for(u16 x = 0; x < Width; ++x)
				set(i++, calc(f, x, y));
	}

	u16 height() const { return Height; }
	u16 width() const { return Width; }

	f32 calc(generate_func f, u16 x, u16 y) const
	{
		const f32 xx = (f32)x - Width*0.5f;
		const f32 yy = (f32)y - Height*0.5f;
		return f((u16)xx, (u16)yy, s);
	}

	// The height at (x, y) is at position y * Width + x.

	void set(u16 x, u16 y, f32 z) { data[y * Width + x] = z; }
	void set(u32 i, f32 z) { data[i] = z; }
	f32 get(u16 x, u16 y) const { return data[y * Width + x]; }

	\endcode  The only difficult part. This considers the normal at (x, y) to
	be the cross product of the vectors between the adjacent points
	in the horizontal and vertical directions.

	s is a scaling factor, which is necessary if the height units are
	different from the coordinate units; for example, if your map has
	heights in metres and the coordinates are in units of a
	kilometer. \code

	vector3df getnormal(u16 x, u16 y, f32 s) const
	{
		const f32 zc = get(x, y);
		f32 zl, zr, zu, zd;

		if (x == 0)
		{
			zr = get(x + 1, y);
			zl = zc + zc - zr;
		}
		else if (x == Width - 1)
		{
			zl = get(x - 1, y);
			zr = zc + zc - zl;
		}
		else
		{
			zr = get(x + 1, y);
			zl = get(x - 1, y);
		}

		if (y == 0)
		{
			zd = get(x, y + 1);
			zu = zc + zc - zd;
		}
		else if (y == Height - 1)
		{
			zu = get(x, y - 1);
			zd = zc + zc - zu;
		}
		else
		{
			zd = get(x, y + 1);
			zu = get(x, y - 1);
		}

		return vector3df(s * 2 * (zl - zr), 4, s * 2 * (zd - zu)).normalize();
	}
};

\endcode  A class which generates a mesh from a heightmap. \code
class TMesh
{
private:
	u16 Width;
	u16 Height;
	f32 Scale;
public:
	SMesh* Mesh;

	TMesh() : Mesh(0), Width(0), Height(0), Scale(1.f)
	{
		Mesh = new SMesh();
	}

	~TMesh()
	{
		Mesh->drop();
	}

	// Unless the heightmap is small, it won't all fit into a single
	// SMeshBuffer. This function chops it into pieces and generates a
	// buffer from each one.

	void init(const HeightMap &hm, f32 scale, colour_func cf, IVideoDriver *driver)
	{
		Scale = scale;

		const u32 mp = driver -> getMaximalPrimitiveCount();
		Width = hm.width();
		Height = hm.height();
		
		const u32 sw = mp / (6 * Height); // the width of each piece

		u32 i=0;
		for(u32 y0 = 0; y0 < Height; y0 += sw)
		{
			u16 y1 = y0 + sw;
			if (y1 >= Height)
				y1 = Height - 1; // the last one might be narrower
			addstrip(hm, cf, y0, y1, i);
			++i;
		}
		if (i<Mesh->getMeshBufferCount())
		{
			// clear the rest
			for (u32 j=i; j<Mesh->getMeshBufferCount(); ++j)
			{
				Mesh->getMeshBuffer(j)->drop();
			}
			Mesh->MeshBuffers.erase(i,Mesh->getMeshBufferCount()-i);
		}

		Mesh->recalculateBoundingBox();
	}

	// Generate a SMeshBuffer which represents all the vertices and
	// indices for values of y between y0 and y1, and add it to the
	// mesh.

	void addstrip(const HeightMap &hm, colour_func cf, u16 y0, u16 y1, u32 bufNum)
	{
		SMeshBuffer *buf = 0;
		if (bufNum<Mesh->getMeshBufferCount())
		{
			buf = (SMeshBuffer*)Mesh->getMeshBuffer(bufNum);
		}
		else
		{
			// create new buffer
			buf = new SMeshBuffer();
			Mesh->addMeshBuffer(buf);
			// to simplify things we drop here but continue using buf
			buf->drop();
		}
		buf->Vertices.set_used((1 + y1 - y0) * Width);

		u32 i=0;
		for (u16 y = y0; y <= y1; ++y)
		{
			for (u16 x = 0; x < Width; ++x)
			{
				const f32 z = hm.get(x, y);
				const f32 xx = (f32)x/(f32)Width;
				const f32 yy = (f32)y/(f32)Height;

				S3DVertex& v = buf->Vertices[i++];
				v.Pos.set(x, Scale * z, y);
				v.Normal.set(hm.getnormal(x, y, Scale));
				v.Color=cf(xx, yy, z);
				v.TCoords.set(xx, yy);
			}
		}

		buf->Indices.set_used(6 * (Width - 1) * (y1 - y0));
		i=0;
		for(u16 y = y0; y < y1; ++y)
		{
			for(u16 x = 0; x < Width - 1; ++x)
			{
				const u16 n = (y-y0) * Width + x;
				buf->Indices[i]=n;
				buf->Indices[++i]=n + Height;
				buf->Indices[++i]=n + Height + 1;
				buf->Indices[++i]=n + Height + 1;
				buf->Indices[++i]=n + 1;
				buf->Indices[++i]=n;
				++i;
			}
		}

		buf->recalculateBoundingBox();
	}
};

\endcode

Our event receiver implementation, taken from tutorial 4.
\code
class MyEventReceiver : public IEventReceiver
{
public:
	// This is the one method that we have to implement
	virtual bool OnEvent(const SEvent& event)
	{
		// Remember whether each key is down or up
		if (event.EventType == irr::EET_KEY_INPUT_EVENT)
			KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

		return false;
	}

	// This is used to check whether a key is being held down
	virtual bool IsKeyDown(EKEY_CODE keyCode) const
	{
		return KeyIsDown[keyCode];
	}

	MyEventReceiver()
	{
		for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
			KeyIsDown[i] = false;
	}

private:
	// We use this array to store the current state of each key
	bool KeyIsDown[KEY_KEY_CODES_COUNT];
};

\endcode

Much of this is code taken from some of the examples. We merely set
up a mesh from a heightmap, light it with a moving light, and allow
the user to navigate around it.
\code

int main(int argc, char* argv[])
{
	// ask user for driver
	video::E_DRIVER_TYPE driverType;

	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;
	}

	MyEventReceiver receiver;
	IrrlichtDevice* device = createDevice(driverType,
			core::dimension2du(800, 600), 32, false, false, false,
			&receiver);

	if(device == 0)
		return 1;
 
	IVideoDriver *driver = device->getVideoDriver();
	ISceneManager *smgr = device->getSceneManager();
	device->setWindowCaption(L"Irrlicht Example for SMesh usage.");

	\endcode

	Create the custom mesh and initialize with a heightmap
	\code
	TMesh mesh;
	HeightMap hm = HeightMap(255, 255);
	hm.generate(eggbox);
	mesh.init(hm, 50.f, grey, driver);

	// Add the mesh to the scene graph
	IMeshSceneNode* meshnode = smgr -> addMeshSceneNode(mesh.Mesh);
	meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);

	// light is just for nice effects
	ILightSceneNode *node = smgr->addLightSceneNode(0, vector3df(0,100,0),
		SColorf(1.0f, 0.6f, 0.7f, 1.0f), 500.0f);
	if (node)
	{
		node->getLightData().Attenuation.set(0.f, 1.f/500.f, 0.f);
		ISceneNodeAnimator* anim = smgr->createFlyCircleAnimator(vector3df(0,150,0),250.0f);
		if (anim)
		{
			node->addAnimator(anim);
			anim->drop();
		}
	}

	ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
	if (camera)
	{
		camera->setPosition(vector3df(-20.f, 150.f, -20.f));
		camera->setTarget(vector3df(200.f, -80.f, 150.f));
		camera->setFarValue(20000.0f);
	}

	\endcode

	Just a usual render loop with event handling. The custom mesh is
	a usual part of the scene graph which gets rendered by drawAll.
	\code
	while(device->run())
	{
		if(!device->isWindowActive())
		{
			device->sleep(100);
			continue;
		}

		if(receiver.IsKeyDown(irr::KEY_KEY_W))
		{
			meshnode->setMaterialFlag(video::EMF_WIREFRAME, !meshnode->getMaterial(0).Wireframe);
		}
		else if(receiver.IsKeyDown(irr::KEY_KEY_1))
		{
			hm.generate(eggbox);
			mesh.init(hm, 50.f, grey, driver);
		}
		else if(receiver.IsKeyDown(irr::KEY_KEY_2))
		{
			hm.generate(moresine);
			mesh.init(hm, 50.f, yellow, driver);
		}
		else if(receiver.IsKeyDown(irr::KEY_KEY_3))
		{
			hm.generate(justexp);
			mesh.init(hm, 50.f, yellow, driver);
		}

		driver->beginScene(true, true, SColor(0xff000000));
		smgr->drawAll();
		driver->endScene();
	}

	device->drop();

	return 0;
}

\endcode

That's it! Just compile and play around with the program.
**/
