IrrLicht is free and open source 3d graphics library written in C++. I came across IrrLicht when I was looking for a graphics library that was simple to understand and easy to follow. I had tried DirectX SDK under Windows a few years ago and remember having to blindly copy-paste code samples to get something to work without being able to understand how the code in front of me worked. I had since read about Ogre3d and couple other graphics engines.
When I found IrrLicht, I was surprised by how soon I was able to get myself up and running. The few tutorials on their site covered enough ground to make me feel confident about spending more time experimenting with the library. In this post, I'll do a walkthrough of some code that I've written.
You'll need to download IrrLicht library or compile it from source. IrrLicht is available for download from their website. It is also available as a download from official repositories of major Linux distributions. IrrLicht is also cross-platform and you'll be able to write 3d graphics applications for Linux, Mac, and Windows.
I use QtCreator to do C++ development but you could use any text editor or IDE of your choice. Just remember to include path to IrrLicht header link against IrrLicht library.
Here is how my Qt project file looks like:
TEMPLATE = app CONFIG += console CONFIG -= qt unix:!macx:!symbian: LIBS += -L/usr/lib/ -lIrrlicht INCLUDEPATH += /usr/include/irrlicht DEPENDPATH += /usr/include/irrlicht SOURCES += \ program.cpp
Add the following lines to your cpp file:
#include <irrlicht.h>
#include <iostream>
using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
int main(int &argc, char ** argv)
{
//Create an Irrlicht Device.
IrrlichtDevice * device = createDevice(EDT_OPENGL,dimension2d
//Get the Scene Manager from the device.
ISceneManager * smgr = device->getSceneManager();
//Get the Video Driver from the device.
IVideoDriver * driver = device->getVideoDriver();
//Add a Cube to the Scene.
ISceneNode * node = smgr->addCubeSceneNode();
//Needed to make the object's texture visible without a light source.
node->setMaterialFlag(EMF_LIGHTING, false);
//Add texture to the cube.
node->setMaterialTexture(0,driver->getTexture("/usr/share/irrlicht/media/wall.jpg"));
//Set cube 100 units further in forward direction (Z axis).
node->setPosition(vector3df(0,0,100));
//Add FPS Camera to allow movement using Keyboard and Mouse.
smgr->addCameraSceneNodeFPS();
//Run simulation
while(device->run())
{
//Begin Scene with a gray backdrop #rgb(125,125,125)
driver->beginScene(true,true,SColor(0,125,125,125));
//Render the scene at this instant.
smgr->drawAll();
//End the scene
driver->endScene();
//Logic to update the scene will go here.
}
return 0;
}
Compile and Run.
You should see a textured cube in the middle of the screen. You will also notice that using your mouse and keyboard, you can move aroud the redered world.
Let's extend this example to do some fancy stuff. How about loading pictures from your computer in a 3d world? Let us do just that. Let's add a routine to to which we'll pass path to a directory as an argument.
We'll traverse this directory for picture files and for each picture file we find, we'll throw a cube on the screen and scale it to right proportion.
#include <irrlicht.h>
#include <iostream>
using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
#define PICSCOUNT 800 //Maximum number of pictures to load.
void processFolder(IrrlichtDevice * device, const path &newDirectory)
{
//Get the File System from the device.
IFileSystem * fs = device->getFileSystem();
//Get the Scene Manager from the device.
ISceneManager * smgr = device->getSceneManager();
//Get the Video Driver from the device.
IVideoDriver * driver = device->getVideoDriver();
//If maximum number of pictures already loaded, then don't load anymore.
if(driver->getTextureCount() >= PICSCOUNT)
{
return;
}
//Change working directory.
fs->changeWorkingDirectoryTo(newDirectory);
//Get List of files and sub folders.
IFileList * flist = fs->createFileList();
//Sort by file names and folder names.
flist->sort();
//Loop through the contents of the working directory.
for(u32 i = 0; i < flist->getFileCount(); i++)
{
//If current item is a directory
if(flist->isDirectory(i))
{
//and it is not "Parent Directory .."
if(strcmp(flist->getFileName(i).c_str(),"..") != 0)
{
//process contents of the current sub directory
processFolder(device,flist->getFullFileName(i));
}
}
else //If current item is a file
{
//Get file name
path filename = flist->getFileName(i);
//Get extension from file name
std::string extension = filename.subString(filename.size() - 4,4).c_str();
//If file extension is .png
if(strcasecmp(extension.data(),".png") == 0)
{
//Create a new cube node with unit dimention
ISceneNode * node = smgr->addCubeSceneNode(1.0f);
//Scale the cube to the dimentions of our liking - 75x107x0.1
node->setScale(vector3df(75,107,0.1f));
//Set random X cordinate between -500 and 500
long x = random()% 1000 - 500;
//Set random Y cordinate between -500 and 500
long y = random()% 1000 - 500;
//Set random Z cordinate between -500 and 500
long z = random()% 1000 - 500;
//Create a position vector
vector3df pos(x,y,z);
//Change cordinates such that direction is preserved and length is 800 units
pos = pos.normalize() * 800.0f;
//Apply new position to cube
node->setPosition(pos);
//Get active camera
ICameraSceneNode * cam = smgr->getActiveCamera();
//Set camera to "look" at cube
cam->setTarget(node->getPosition());
//Apply camera's new rotation (as a result of above) to the node
node->setRotation(cam->getRotation());
//Make cube's texture visible without light
node->setMaterialFlag(EMF_LIGHTING, false);
//Set the file's graphics as texture to the cube
node->setMaterialTexture(0,driver->getTexture(flist->getFullFileName(i).c_str()));
//If maximum number of pictures already loaded, then don't load anymore.
if(driver->getTextureCount() >= PICSCOUNT)
{
return;
}
}
}
}
}
int main(int &argc, char ** argv)
{
//Create an Irrlicht Device.
IrrlichtDevice * device = createDevice(EDT_OPENGL,dimension2d(800,600));
//Get the Scene Manager from the device.
ISceneManager * smgr = device->getSceneManager();
//Get the Video Driver from the device.
IVideoDriver * driver = device->getVideoDriver();
//Add FPS Camera to allow movement using Keyboard and Mouse.
smgr->addCameraSceneNodeFPS();
//Process contents of this folder.
processFolder(device, "/home/karim/Images/Cards-Large/150/");
//Run simulation
while(device->run())
{
//Begin Scene with a gray backdrop #rgb(125,125,125)
driver->beginScene(true,true,SColor(0,125,125,125));
//Render the scene at this instant.
smgr->drawAll();
//End the scene
driver->endScene();
//Logic to update the scene will go here.
}
return 0;
}
Let's extend this example to do some fancy stuff. How about loading pictures from your computer in a 3d world? Let us do just that. Let's add a routine to to which we'll pass path to a directory as an argument.
We'll traverse this directory for picture files and for each picture file we find, we'll throw a cube on the screen and scale it to right proportion.
#include <irrlicht.h>
#include <iostream>
using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
#define PICSCOUNT 800 //Maximum number of pictures to load.
void processFolder(IrrlichtDevice * device, const path &newDirectory)
{
//Get the File System from the device.
IFileSystem * fs = device->getFileSystem();
//Get the Scene Manager from the device.
ISceneManager * smgr = device->getSceneManager();
//Get the Video Driver from the device.
IVideoDriver * driver = device->getVideoDriver();
//If maximum number of pictures already loaded, then don't load anymore.
if(driver->getTextureCount() >= PICSCOUNT)
{
return;
}
//Change working directory.
fs->changeWorkingDirectoryTo(newDirectory);
//Get List of files and sub folders.
IFileList * flist = fs->createFileList();
//Sort by file names and folder names.
flist->sort();
//Loop through the contents of the working directory.
for(u32 i = 0; i < flist->getFileCount(); i++)
{
//If current item is a directory
if(flist->isDirectory(i))
{
//and it is not "Parent Directory .."
if(strcmp(flist->getFileName(i).c_str(),"..") != 0)
{
//process contents of the current sub directory
processFolder(device,flist->getFullFileName(i));
}
}
else //If current item is a file
{
//Get file name
path filename = flist->getFileName(i);
//Get extension from file name
std::string extension = filename.subString(filename.size() - 4,4).c_str();
//If file extension is .png
if(strcasecmp(extension.data(),".png") == 0)
{
//Create a new cube node with unit dimention
ISceneNode * node = smgr->addCubeSceneNode(1.0f);
//Scale the cube to the dimentions of our liking - 75x107x0.1
node->setScale(vector3df(75,107,0.1f));
//Set random X cordinate between -500 and 500
long x = random()% 1000 - 500;
//Set random Y cordinate between -500 and 500
long y = random()% 1000 - 500;
//Set random Z cordinate between -500 and 500
long z = random()% 1000 - 500;
//Create a position vector
vector3df pos(x,y,z);
//Change cordinates such that direction is preserved and length is 800 units
pos = pos.normalize() * 800.0f;
//Apply new position to cube
node->setPosition(pos);
//Get active camera
ICameraSceneNode * cam = smgr->getActiveCamera();
//Set camera to "look" at cube
cam->setTarget(node->getPosition());
//Apply camera's new rotation (as a result of above) to the node
node->setRotation(cam->getRotation());
//Make cube's texture visible without light
node->setMaterialFlag(EMF_LIGHTING, false);
//Set the file's graphics as texture to the cube
node->setMaterialTexture(0,driver->getTexture(flist->getFullFileName(i).c_str()));
//If maximum number of pictures already loaded, then don't load anymore.
if(driver->getTextureCount() >= PICSCOUNT)
{
return;
}
}
}
}
}
int main(int &argc, char ** argv)
{
//Create an Irrlicht Device.
IrrlichtDevice * device = createDevice(EDT_OPENGL,dimension2d
//Get the Scene Manager from the device.
ISceneManager * smgr = device->getSceneManager();
//Get the Video Driver from the device.
IVideoDriver * driver = device->getVideoDriver();
//Add FPS Camera to allow movement using Keyboard and Mouse.
smgr->addCameraSceneNodeFPS();
//Process contents of this folder.
processFolder(device, "/home/karim/Images/Cards-Large/150/");
//Run simulation
while(device->run())
{
//Begin Scene with a gray backdrop #rgb(125,125,125)
driver->beginScene(true,true,SColor(0,125,125,125));
//Render the scene at this instant.
smgr->drawAll();
//End the scene
driver->endScene();
//Logic to update the scene will go here.
}
return 0;
}
The images will appear as though they are spread around and stuck on the inside/outside of a transparent sphere.
Let change it a little. Make the following changes:
From:
//Change cordinates such that direction is preserved and length is 800 units
pos = pos.normalize() * 800.0f;
To:
//Set Y cordinate to 0
pos.Y = 0;
From:
//Change cordinates such that direction is preserved and length is 800 units
pos = pos.normalize() * 800.0f;
To:
//Set Y cordinate to 0
pos.Y = 0;
The images will now appear as though they are spread around like the stone henge. How about a little more fun? Make the following change:
From:
//Set Y cordinate to 0
pos.Y = 0;
To:
//Set Y cordinate to 0
//pos.Y = 0; //Comment
From:
//Set Y cordinate to 0
pos.Y = 0;
To:
//Set Y cordinate to 0
//pos.Y = 0; //Comment
I hope you this tutorial proves helpful in getting you started in your journey through the facinating world of 3d graphics programming.