Irrlicht / Qt4 integration + QtCreator

A forum to store posts deemed exceptionally wise and useful
NickLH
Posts: 7
Joined: Wed Aug 24, 2011 7:12 am

Irrlicht / Qt4 integration + QtCreator

Post by NickLH »

Hello everyone, over the last couple of days I had been looking at how to integrate Irrlicht with a GUI such as wxWidgets, CEGUI, or Qt. It seems like doing this on windows is relatively easy, but for Linux (my preferred OS), very little information can be found. After trying for many hours to get wxWidgets and CEGUI running, I gave up on both of them. I then turned to Qt4 and immediately encountered the problem that I could not make the Irrlicht window render inside a Qt4 widget. The Irrlicht window was always being created in its own separate window. On top of that, I found it extremely hard to get QtCreator files to work with my code. After looking for many hours, I came across this post (http://www.irrlicht.ru/forum/viewtopic.php?f=1&t=208), in which Digan posted some code which showed how to get Qt4 and Irrlicht working on Linux. I can't thank you enough for that Digan . :D

I am posting my version of the code (translated from the Russian version that Digan posted), with some extra comments and some information about how to get the code to compile in Code::Blocks. The information here has been tested with Code::Blocks 10.05, Irrlicht 1.7.2, qt4, and Ubuntu 10.10. I do not know if the steps outlined here can be of any use on any other builds or compilers, but I imagine that the steps will be similar.

1) First thing to do is create a new Qt4 Code::Blocks project. Setup your include directories, and library files so that Code::Blocks can find Irrlicht.

2) Now we need to create a new helper tool which allows Qt to use signals and slots (see here for more information http://doc.qt.nokia.com/latest/moc.html).
2a) Click the "Tools" menu, then select "Configure Tools...".
2b) Enter the following information:
Name: MOC
Executable: /usr/bin/moc
Parameters: ${ACTIVE_EDITOR_FILENAME} -o moc_${ACTIVE_EDITOR_STEM}.cpp
Working directory: ${PROJECT_DIR}
2c) Now click "Ok".

3) Now we need to open up QtCreator and create a new project. Make the window dimensions 800x600 or something similar to that.
3a) Build the QtCreator project to see if the GUI looks right.
3b) We now need to make the .ui file that QtCreator generates into a file that can be read into our program. To do that, open a terminal and enter the following without the quotes "uic filename.ui > ui_filename.h". In my case, I had "uic MainWindow.ui > ui_MainWindow.h".
3c) Include ui_filename.h into your Code::Blocks project.

Here is my ui_MainWindow.h file :

Code: Select all

 
/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created: Wed Aug 24 01:33:57 2011
**      by: Qt User Interface Compiler version 4.7.0
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
 
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QButtonGroup>
#include <QtGui/QHeaderView>
#include <QtGui/QMainWindow>
#include <QtGui/QMenuBar>
#include <QtGui/QStatusBar>
#include <QtGui/QToolBar>
#include <QtGui/QWidget>
 
QT_BEGIN_NAMESPACE
 
class Ui_MainWindow
{
public:
    QWidget *centralWidget;
    QMenuBar *menuBar;
    QToolBar *mainToolBar;
    QStatusBar *statusBar;
 
    void setupUi(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
        MainWindow->resize(800, 600);
        centralWidget = new QWidget(MainWindow);
        centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
        MainWindow->setCentralWidget(centralWidget);
        menuBar = new QMenuBar(MainWindow);
        menuBar->setObjectName(QString::fromUtf8("menuBar"));
        menuBar->setGeometry(QRect(0, 0, 800, 25));
        MainWindow->setMenuBar(menuBar);
        mainToolBar = new QToolBar(MainWindow);
        mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));
        MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
        statusBar = new QStatusBar(MainWindow);
        statusBar->setObjectName(QString::fromUtf8("statusBar"));
        MainWindow->setStatusBar(statusBar);
 
        retranslateUi(MainWindow);
 
        QMetaObject::connectSlotsByName(MainWindow);
    } // setupUi
 
    void retranslateUi(QMainWindow *MainWindow)
    {
        MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0, QApplication::UnicodeUTF8));
    } // retranslateUi
 
};
 
namespace Ui {
    class MainWindow: public Ui_MainWindow {};
} // namespace Ui
 
QT_END_NAMESPACE
 
#endif // MAINWINDOW_H
 
4) We are now going to create the Qt Irrlicht widget.
4a) Create two files "QIrrlichtWidget.h" and "QIrrlichtWidget.cpp"
4b) Paste the following codes into the files. I won't explain what is going on because I think everything is fairly well documented.

Code: Select all

 
// QIrrlichtWidget.h
 
#ifndef QIRRLICHTWIDGET_H
#define QIRRLICHTWIDGET_H
 
#include <QWidget>
#include <QResizeEvent>
#include <irrlicht.h>
 
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
 
// Our Irrlicht rendering widget
// To have everything compile, we need to run MOC on this file
class QIrrlichtWidget : public QWidget
{
    // Macro for the meta-object compiler MOC
    Q_OBJECT
 
public:
    explicit QIrrlichtWidget(QWidget *parent = 0);
    ~QIrrlichtWidget();
 
    // Returns a pointer to the Irrlicht Device
    IrrlichtDevice* getIrrlichtDevice();
 
    // Create the Irrlicht device and connect the signals and slots
    void init();
 
signals:
    // Signal that its time to update the frame
    void updateIrrlichtQuery(IrrlichtDevice* device);
 
public slots:
    // Function called in response to updateIrrlichtQuery. Renders the scene in the widget
    void updateIrrlicht(IrrlichtDevice* device);
 
protected:
    virtual void paintEvent(QPaintEvent* event);
    virtual void timerEvent(QTimerEvent* event);
    virtual void resizeEvent(QResizeEvent* event);
 
    IrrlichtDevice *device;
    // We keep the camera inside this widget so we can resize the window dynamically
    ICameraSceneNode* camera;
};
 
#endif // QIRRWIDGET_H
 

Code: Select all

 
// QIrrlichtWidget.cpp
 
#include "QIrrlichtWidget.h"
#include <QtCore/QDebug>
 
QIrrlichtWidget::QIrrlichtWidget(QWidget *parent) :
    QWidget(parent)
{
    // Indicates that the widget wants to draw directly onto the screen. (From documentation : http://doc.qt.nokia.com/latest/qt.html)
    // Essential to have this or there will be nothing displayed
    setAttribute(Qt::WA_PaintOnScreen);
    // Indicates that the widget paints all its pixels when it receives a paint event.
    // Thus, it is not required for operations like updating, resizing, scrolling and focus changes to erase the widget before generating paint events.
    // Not sure this is required for the program to run properly, but it is here just incase.
    setAttribute(Qt::WA_OpaquePaintEvent);
    // Widget accepts focus by both tabbing and clicking
    setFocusPolicy(Qt::StrongFocus);
    // Not sure if this is necessary, but it was in the code I am basing this solution off of
    setAutoFillBackground(false);
 
    device = 0;
}
 
QIrrlichtWidget::~QIrrlichtWidget()
{
    if(device != 0)
    {
        device->closeDevice();
        device->drop();
    }
}
 
// Create the Irrlicht device and connect the signals and slots
void QIrrlichtWidget::init()
{
    // Make sure we can't create the device twice
    if(device != 0)
        return;
 
    // Set all the device creation parameters
    SIrrlichtCreationParameters params;
    params.AntiAlias = 0;
    params.Bits = 32;
    params.DeviceType = EIDT_X11;
    params.Doublebuffer = true;
    params.DriverType = EDT_OPENGL;
    params.EventReceiver = 0;
    params.Fullscreen = false;
    params.HighPrecisionFPU = false;
    params.IgnoreInput = false;
    params.LoggingLevel = ELL_INFORMATION;
    params.Stencilbuffer = true;
    params.Stereobuffer = false;
    params.Vsync = false;
    // Specify which window/widget to render to
    params.WindowId = reinterpret_cast<void*>(winId());
    params.WindowSize.Width = width();
    params.WindowSize.Height = height();
    params.WithAlphaChannel = false;
    params.ZBufferBits = 16;
 
    // Create the Irrlicht Device with the previously specified parameters
    device = createDeviceEx(params);
 
    if(device)
    {
        // Create a camera so we can view the scene
        camera = device->getSceneManager()->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));
    }
 
    // Connect the update signal (updateIrrlichtQuery) to the update slot (updateIrrlicht)
    connect(this, SIGNAL(updateIrrlichtQuery(IrrlichtDevice*)), this, SLOT(updateIrrlicht(IrrlichtDevice*)));
 
    // Start a timer. A timer with setting 0 will update as often as possible.
    startTimer(0);
}
 
IrrlichtDevice* QIrrlichtWidget::getIrrlichtDevice()
{
    return device;
}
 
void QIrrlichtWidget::paintEvent(QPaintEvent* event)
{
    if(device != 0)
    {
        emit updateIrrlichtQuery(device);
    }
}
 
void QIrrlichtWidget::timerEvent(QTimerEvent* event)
{
    // Emit the render signal each time the timer goes off
    if (device != 0)
    {
        emit updateIrrlichtQuery(device);
    }
 
    event->accept();
}
 
void QIrrlichtWidget::resizeEvent(QResizeEvent* event)
{
    if(device != 0)
    {
        dimension2d<u32> widgetSize;
        widgetSize.Width = event->size().width();
        widgetSize.Height = event->size().height();
        device->getVideoDriver()->OnResize(widgetSize);
 
        ICameraSceneNode *cam = device->getSceneManager()->getActiveCamera();
        if (cam != 0)
        {
            cam->setAspectRatio((f32)widgetSize.Height / (f32)widgetSize.Width);
        }
    }
 
    QWidget::resizeEvent(event);
}
 
void QIrrlichtWidget::updateIrrlicht( irr::IrrlichtDevice* device )
{
    if(device != 0)
    {
        device->getTimer()->tick();
 
        SColor color (255,100,100,140);
 
        device->getVideoDriver()->beginScene(true, true, color);
        device->getSceneManager()->drawAll();
        device->getVideoDriver()->endScene();
    }
 
}
 
// Include the extra Qt file for signals and slots
#include "moc_QIrrlichtWidget.cpp"
 
Notice in the .cpp file, the last line has #include "moc_QIrrlichtWidget.cpp". That file will be generated by the MOC tool we created earlier.
If you get an error stating that your structure is incomplete, it is because you are including the moc file at the wrong place. It must go inside the .cpp file, not the .h file.

5) Now we need three more files "MainApplicationWindow.h", "MainApplicationWindow.cpp", "main.cpp". Paste the following codes.

Code: Select all

 
// MainApplicationWindow.h
 
// Make sure not to have the same define name as in ui_MainWindow.h
// This gives an incomplete structure error
#ifndef MAINAPPLICATIONWINDOW_H
#define MAINAPPLICATIONWINDOW_H
 
#include <QMainWindow>
#include <QWidget>
#include "ui_MainWindow.h"
#include "QIrrlichtWidget.h"
 
// Include the namespace from ui_MainWindow.h
namespace Ui {
    class MainWindow;
}
 
// Class that contains the main window as well as the irrlicht widget
// We use the basic class constructued by QtCreator and modify it
// To have everything compile, we need to run MOC on this file
class MainApplicationWindow : public QMainWindow
{
    // Macro for the meta-object compiler MOC
    Q_OBJECT
 
public:
    explicit MainApplicationWindow(QWidget *parent = 0);
    ~MainApplicationWindow();
 
    QIrrlichtWidget* getIrrlichtWidget(){return &irrWidget;}
 
private:
    Ui::MainWindow *ui;
    QIrrlichtWidget irrWidget;
};
 
#endif // MAINAPPLICATIONWINDOW_H
 

Code: Select all

 
// MainApplicationWindow.cpp
 
// Use the file generated by QtCreate and modify it to accomodate the irrlicht widget
#include "MainApplicationWindow.h"
 
MainApplicationWindow::MainApplicationWindow(QWidget *parent) :
    QMainWindow(parent),
   ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    // Setup for the Irrlicht Widget
    // This is new code that was not in the file generated by QtCreate
    irrWidget.setParent(ui->centralWidget);
    irrWidget.setGeometry(160, 0, 630, 480);
}
 
MainApplicationWindow::~MainApplicationWindow()
{
    delete ui;
}
 
// Include the file generated by running MOC on MainApplicationWindow.h
#include "moc_MainApplicationWindow.cpp"
 
Once again we need to include a moc file at the end of MainApplicationWindow.cpp.

Code: Select all

 
// main.cpp
 
#include <QApplication>
#include <QFont>
#include <QPushButton>
 
#include "MainApplicationWindow.h"
 
int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
 
    MainApplicationWindow mainWindow;
    mainWindow.show();
    mainWindow.getIrrlichtWidget()->init();
 
    // Make sure the Irrlicht Device exists before trying to use it
    if(mainWindow.getIrrlichtWidget()->getIrrlichtDevice())
    {
        ISceneManager *smgr = mainWindow.getIrrlichtWidget()->getIrrlichtDevice()->getSceneManager();
        IVideoDriver *driver = mainWindow.getIrrlichtWidget()->getIrrlichtDevice()->getVideoDriver();
 
        // Just display a simple mesh
        IAnimatedMesh* mesh = smgr->getMesh("sydney.md2");
        if (!mesh)
        {
            mainWindow.getIrrlichtWidget()->getIrrlichtDevice()->drop();
            return 1;
        }
 
        IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
        if (node)
        {
            node->setMaterialFlag(EMF_LIGHTING, false);
            node->setMD2Animation(scene::EMAT_STAND);
            node->setMaterialTexture( 0, driver->getTexture("media/sydney.bmp") );
        }
    }
 
    return app.exec();
}
 
6) We are now ready to compile the project.
6a) Select QIrrlichtWidget.h and run the MOC tool on that file. This should generate a file named "moc_QIrrlichtWidget.cpp". If the filename differs, modify the code accordingly, or change the file name.
6b) Select MainApplicationWindow.h and run the MOC tool on that file. Every time that a class contains a Q_OBJECT macro, you must run MOC on it.
6c) Build the project as you would normally, and you're done!

If everything went well, you should be able to see a 800x600 window, with a widget that displays an animated mesh. Adding new controls to this project is now easy. Just go into QtCreator, add the widgets, save the .ui file, run uic on it as stated in part 3, rerun MOC if you change any signals or slots, and you're done!


Wow. Sorry for the super long post. I wanted to make sure this was through enough, because I found that figuring this all out myself was a huge hassle. Hopefully this will help someone along and spare them hours of frustration. I will probably put this up on the Wiki once some people comment and tell me if I've made any big mistakes.

Any questions and comments are welcome, and if you find a bug in my code, or something I missed, please pm me so I can look into it and fix it.
Escen
Competition winner
Posts: 167
Joined: Sun Jul 19, 2009 11:27 am
Location: the Netherlands
Contact:

Re: Irrlicht / Qt4 integration + QtCreator

Post by Escen »

Thanks NickLH for sharing, didn't try it yet but I do appreciate your post. I'll save it for later...
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by Virion »

It works!!
Thanks!! I have been dealing with X11 errors for days now and you saved my day! XD
I didn't entirely use your method though because I am running my project on QtCreator. instead of rendering irrlicht in the entire window, I make it render within a small frame because I am doing a level editor. i have looked at your code and find out my method was slightly wrong. THANKS AGAIN!!

Mine is slightly different:

Code: Select all

MainWindow window;
 
QIrrlichtWidget renderer;
 
QWidget* container = window.GetContainer(); // will return a QWidget from the ui::
QVBoxLayout* layout = (QVBoxLayout*)container->layout();
layout->addWidget(&renderer);
 
window.show();
renderer.init();
Image
image hosting
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by Virion »

by the way I just realize the resizeEvent doesn't work. probably because I am using an auto expanding layout? :( Sydney becomes stretched when I resize the window. :oops:
aanderse
Posts: 155
Joined: Sun Aug 10, 2008 2:02 pm
Location: Canada

Re: Irrlicht / Qt4 integration + QtCreator

Post by aanderse »

virion

strangely i can't determine which os you are running from your screenshot... but i have to assume it is gnu/linux of some sort running on x11.
if i am correct you need to include the following:

Code: Select all

 
#ifdef Q_WS_X11
 
#include <X11/Xlib.h>
 
#endif
 
and then add something like this to the resizeEvent of the qirrlichtwidget:

Code: Select all

 
#ifdef Q_WS_X11
 
XResizeWindow ((Display *) device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display, device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Window, size.Width, size.Height);
 
#endif
 
i hope this helps you
NickLH
Posts: 7
Joined: Wed Aug 24, 2011 7:12 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by NickLH »

Glad to hear it works Virion :D

I checked and you're right, the resize event does not work properly for me either. Although my irrlicht window stays the same size and just gets moved around, hiding parts of it (sydney doesn't get stretched). I'm looking into that issue right now.

Would you mind posting how you setup QtCreator to run with Irrlicht, and how to compile the code with QtCreator? I gave up on it after a while, but I would much rather work in that environment given that it would make the whole build process much more streamlined.
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by Virion »

it's not that hard to set up irrlicht on QtCreator. you open up your .pro file in QtCreator, right click on the script editor window and you will see "Add Library". Choose external library and then set the folder paths like you would do in code::blocks. QtCreator will generate the code for .pro file for you. automated. :)
NickLH
Posts: 7
Joined: Wed Aug 24, 2011 7:12 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by NickLH »

Thanks for the info. It worked perfectly.
No more playing around with pesky moc files or uic :)
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by Virion »

aanderse wrote:virion

strangely i can't determine which os you are running from your screenshot... but i have to assume it is gnu/linux of some sort running on x11.
if i am correct you need to include the following:

Code: Select all

 
#ifdef Q_WS_X11
 
#include <X11/Xlib.h>
 
#endif
 
and then add something like this to the resizeEvent of the qirrlichtwidget:

Code: Select all

 
#ifdef Q_WS_X11
 
XResizeWindow ((Display *) device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display, device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Window, size.Width, size.Height);
 
#endif
 
i hope this helps you
my program crash after insert this code. :( by the way i'm using PepperMint OS (ubuntu variant with LXDE desktop environment)
aanderse
Posts: 155
Joined: Sun Aug 10, 2008 2:02 pm
Location: Canada

Re: Irrlicht / Qt4 integration + QtCreator

Post by aanderse »

Virion wrote:my program crash after insert this code. :(
if you paste a simplified version of your project i can take a look at it for you
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by Virion »

aanderse
Posts: 155
Joined: Sun Aug 10, 2008 2:02 pm
Location: Canada

Re: Irrlicht / Qt4 integration + QtCreator

Post by aanderse »

i've taken a look at your code and the reason it crashes is because you don't
check for a null device before you call XWindowResize

i've also noticed that with the current irrlicht svn version a call to
XWindowResize is no longer necessary... you can remove that call

so it looks like your actual problem has to do with aspect ratio or zoom

try flipping around the height and width in your setAspectRatio so it looks like:

Code: Select all

cam->setAspectRatio((f32)widgetSize.Width / (f32)widgetSize.Height);
if that isn't what you're looking for... what exactly would you like the
irrlicht view to do when you resize the window? remain at the current zoom level
and just show more of the scene?
NickLH
Posts: 7
Joined: Wed Aug 24, 2011 7:12 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by NickLH »

Thanks aanderse, I can't believe that I overlooked that! I was setting the aspect ratio all wrong. It now retains the same zoom level and shows more of the screen, which is exactly what I wanted to do!
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: Irrlicht / Qt4 integration + QtCreator

Post by Virion »

it works perfectly now. appreciate both of your help aanderse and NickLH. thanks! :D
YankzzDev
Posts: 23
Joined: Wed Oct 05, 2011 4:37 am
Location: Indonesia

Re: Irrlicht / Qt4 integration + QtCreator

Post by YankzzDev »

it's work on windows? i want integration to windows
Nobody is Perfect!!
Post Reply