Skip to main content

History: Integrating Ogre into QT5

Source of version: 17

Copy to clipboard
            __Work in Progress__

Original forum link: http://www.ogre3d.org/forums/viewtopic.php?p=515314#p515314

The following document is an up-to-date method of integrating Ogre3D into the latest version of Qt (5.4 as of this writing).

Previous methods of integrating Ogre3D with Qt focused on [http://www.ogre3d.org/tikiwiki/QtOgre|using a QWidget] and overriding the paint methods for drawing.

The method proposed in this document uses a QWindow as opposed to a QWidget. There are several reasons for using a QWindow over a QWidget:

*__Smaller memory and processing footprint__. Simply put, [http://stackoverflow.com/questions/17860604/what-is-the-difference-between-a-qwindow-and-qwidget|QWidgets use a lot of resources in comparison to QWindows] and thus adds a lot of unnecessary overhead. Ogre3D doesn't require much from Qt besides a surface to draw upon and the input/window events from the underlying OS and windowing system that a QWindow provides. As a related sidenote, QWidget actually inherits from QWindow as shown in the below inheritance chain.
*__Easier integration__. Because of the simplicity of QWindow it is actually much easier to integrate Ogre3D into Qt using a QWindow versus using a QWidget. QWidget by default creates a surface to draw on which Ogre3D doesn't need to draw upon. Thus, as shown in earlier Qt-Ogre3D implementation documents you need to override Qt methods to prevent the default QWidget methods from "drawing over" Ogre3D. Using a QWindow this is unnecessary as with a QWindow no "surface" is automatically created; you can simply supply QWindow's native "Window ID" (whether it is a HWND on Windows or the equivalent representations on Linux/Mac OS) and Ogre3D takes care of the rest.
*__All standard Qt functionality can still be implemented__. If you still need to have a QWidget (for integration into a QMainWindow/QTabWidget/etc.) this can still be achieved. You can easily create a QWidget "container" for your QWindow and get all of the standard benefits of a QWidget. A standard use case for this would be needing to place an Ogre3D-integrated QWindow inside of a standard QMainWindow as a "central widget".

Note that the below implementation depends on you following the guides on setting up include/library/runtime binary directories. Since the setup for this is compiler and operating system dependent such details is beyond the scope of this document.

In summary, [http://www.ogre3d.org/tikiwiki/Setting+Up+An+Application+-+Visual+Studio|consult a guide like this] for setting up directory paths, includes, etc. Note that you will need to do this for Qt as well so consult equivalent documentation for Qt Creator [http://stackoverflow.com/questions/2752352/how-to-add-include-path-in-qt-creator|like this].

With the proper setup of the environment done, we need to create a header file to include the necessary Qt components as well as including Ogre3D:
{CODE(wrap="1", colors="c++")}
#ifndef QTOGREWINDOW_H_
#define QTOGREWINDOW_H_

/*

For the purposes of creating a bare-bones Qt implementation I found that I only need to include these three (3) Qt headers. 

*/

#include <QtWidgets/QApplication>
#include <QtGui/QKeyEvent>
#include <QtGui/QWindow>

/*

As expected; the Ogre3D header.

*/

#include "OGRE/Ogre.h"

/*

With the headers included we now need to inherit from QWindow.

*/

class QTOgreWindow : public QWindow {


/*

A QWindow still inherits from QObject and can have signals/slots; we need to add the appropriate Q_OBJECT keyword so that Qt's intermediate compiler can do the necessary wireup between our class and the rest of Qt.

*/

	Q_OBJECT

public:

	explicit QTOgreWindow(QObject *parent = NULL);
	~QTOgreWindow();

/*

We declare these methods virtual to allow for further inheritance.

*/

	virtual void render(QPainter *painter);
	virtual void render();

	virtual void initialize();

	void setAnimating(bool animating);

public slots:

	virtual void renderLater();
	virtual void renderNow();

/*

We use an event filter to be able to capture keyboard/mouse events. More on this later.

*/


	virtual bool eventFilter(QObject *target, QEvent *event);

protected:

/*

Ogre3D pointers added here. Useful to have the pointers here for use by the window later.

*/

	Ogre::Root* ogreRoot;
	Ogre::RenderWindow* ogreWindow;
	Ogre::SceneManager* ogreSceneMgr;
	Ogre::Camera* ogreCamera;
	Ogre::Viewport* ogreViewPort;

	bool m_update_pending;
	bool m_animating;

/*

The below methods are what is actually fired when they keys on the keyboard are hit.

Similar events are fired when the mouse is pressed or other events occur.

*/

	virtual void keyPressEvent(QKeyEvent * ev);
	virtual void keyReleaseEvent(QKeyEvent * ev);
	virtual void exposeEvent(QExposeEvent *event);
	virtual bool event(QEvent *event);

private:

private slots:

};

#endif
{CODE}

With the header prepared, it's now time to implement the class:

{CODE(wrap="1", colors="c++")}
#include "QTOgreWindow.h"

/*

Note that we pass any supplied QWindow parent to the base QWindow class. This is necessary should we need to use our class within a container.

More on that later.

*/


QTOgreWindow::QTOgreWindow(QWindow *parent) : QWindow(parent)
	, m_update_pending(false)
	, m_animating(false)
{

	setAnimating(true);
	installEventFilter(this);

}

/*

Upon destruction of the QWindow object we destroy the Ogre3D scene.

*/

QTOgreWindow::~QTOgreWindow()
{
	delete ogreRoot;
}

/*

In case any drawing surface backing stores (QRasterWindow or QOpenGLWindow) of Qt are supplied to this class in any way we inform Qt that they will be unused.

*/

void QTOgreWindow::render(QPainter *painter)
{
	Q_UNUSED(painter);
}

/*

Our initialization function. Called by our renderNow() function once when the window is first exposed.

*/

void QTOgreWindow::initialize()
{

/*

As shown Ogre3D is initialized normally; just like in other documentation.

*/

	ogreRoot = new Ogre::Root(Ogre::String("plugins.cfg"));
	Ogre::ConfigFile ogreConfig;

/*

Commended out for simplicity but should you need to initialize resources you can do so normally.

	ogreConfig.load("resources/resource_configs/resources.cfg");

	Ogre::ConfigFile::SectionIterator seci = ogreConfig.getSectionIterator();

	Ogre::String secName, typeName, archName;
	while (seci.hasMoreElements()) {
		secName = seci.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
		Ogre::ConfigFile::SettingsMultiMap::iterator i;
		for (i = settings->begin(); i != settings->end(); ++i) {
			typeName = i->first;
			archName = i->second;
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
				archName, typeName, secName);
		}
	}

*/

/*

I found that only OpenGL seems to work with Qt; event when on Microsoft Windows.  The application crashes when I tried DirectX rendering. Thus, I set the render system to OpenGL in accordance with the supplied "plugins.cfg" file.

*/

	ogreRoot->setRenderSystem(ogreRoot->getAvailableRenderers()[0]);
	ogreRoot->initialise(false);

	Ogre::NameValuePairList parameters;


/*

Flag within the parameters set so that Ogre3D initializes an OpenGL context on it's own.

*/

	parameters["currentGLContext"] = Ogre::String("false");

/*

We need to supply the low level OS window handle to this QWindow so that Ogre3D knows where to draw the scene. Below is a cross-platform method on how to do this.

*/

#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
	parameters["externalWindowHandle"] = Ogre::StringConverter::toString((size_t) (this->winId()));
#else
	parameters["externalWindowHandle"] = Ogre::StringConverter::toString((unsigned long)(this->winId()));
#endif

#if defined(Q_OS_MAC)
	parameters["macAPI"] = "cocoa";
	parameters["macAPICocoaUseNSView"] = "true";
#endif

/*

Note below that we supply the creation function for the Ogre3D window the width and height from the current QWindow object using the "this" pointer.

*/

	ogreWindow = ogreRoot->createRenderWindow("QT Window", this->width(), this->height(), false, &parameters);
	ogreWindow->setVisible(true);

/*

The rest of the code in the initialization function is standard Ogre3D scene code. Consult other tutorials for specifics.

*/

	ogreSceneMgr = ogreRoot->createSceneManager(Ogre::ST_GENERIC);
	ogreCamera = ogreSceneMgr->createCamera("MainCamera");

	ogreCamera->setPosition(Ogre::Vector3(0.0f, 0.0f, 10.0f));
	ogreCamera->lookAt(Ogre::Vector3(0.0f, 0.0f, -300.0f));
	ogreCamera->setNearClipDistance(0.1f);
	ogreCamera->setFarClipDistance(200.0f);

	ogreViewPort = ogreWindow->addViewport(ogreCamera);
	ogreViewPort->setBackgroundColour(Ogre::ColourValue(0.4f, 0.3f, 0.1f));

	ogreCamera->setAspectRatio(Ogre::Real(ogreViewPort->getActualWidth()) / Ogre::Real(ogreViewPort->getActualHeight()));

	Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);

	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

	ogreSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));
	
	Ogre::Entity* sphereMesh = ogreSceneMgr->createEntity("mySphere", Ogre::SceneManager::PT_SPHERE);

	Ogre::SceneNode* childSceneNode = ogreSceneMgr->getRootSceneNode()->createChildSceneNode();

	childSceneNode->attachObject(sphereMesh);

	Ogre::MaterialPtr sphereMaterial = Ogre::MaterialManager::getSingleton().create("SphereMaterial",
	Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true );

	sphereMaterial->getTechnique(0)->getPass(0)->setAmbient(0.1f, 0.1f, 0.1f);
	sphereMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.2f, 0.2f, 0.2f, 1.0f);
	sphereMaterial->getTechnique(0)->getPass(0)->setSpecular(0.9f, 0.9f, 0.9f, 1.0f);
	//sphereMaterial->setAmbient(0.2f, 0.2f, 0.5f);
	//sphereMaterial->setSelfIllumination(0.2f, 0.2f, 0.1f);

	sphereMesh->setMaterialName("SphereMaterial");
	childSceneNode->setPosition(Ogre::Vector3(0.0f, 0.0f, 0.0f));
	childSceneNode->setScale(Ogre::Vector3(0.01f, 0.01f, 0.01f)); // Radius, in theory.

	Ogre::Light* light = ogreSceneMgr->createLight("MainLight");
	light->setPosition(20.0f, 80.0f, 50.0f);

}

void QTOgreWindow::render()
{

/*

How we tied in the render function for OGre3D with QWindow's render function. This is what gets call repeatedly. Note that we don't call this function directly; rather we use the renderNow() function to call this method as we don't want to render the Ogre3D scene unless everything is set up first.

That is what renderNow() does.

Theoretically you can have one function that does this check but from my experience it seems better to keep things separate and keep the render function as simple as possible.

*/
	
	Ogre::WindowEventUtilities::messagePump();
	ogreRoot->renderOneFrame();

}

void QTOgreWindow::renderLater()
{

/*

This function forces QWindow to keep rendering. Omitting this causes the renderNow() function to only get called when the window is resized, moved, etc. as opposed to all of the time; which is generally what we need.

*/

	if (!m_update_pending) {
		m_update_pending = true;
		QApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
	}
}

bool QTOgreWindow::event(QEvent *event)
{

/*

QWindow's "message pump". The base method that handles all QWindow events. As you will see there are other methods that actually process the keyboard/other events of Qt and the underlying OS.

Note that we call the renderNow() function which checks to see if everything is initialized, etc. before calling the render() function.

*/

	switch (event->type()) {
	case QEvent::UpdateRequest:
		m_update_pending = false;
		renderNow();
		return true;
	default:
		return QWindow::event(event);
	}
}

/*

Called after the QWindow is reopened or when the QWindow is first opened.

*/

void QTOgreWindow::exposeEvent(QExposeEvent *event)
{
	Q_UNUSED(event);

	if (isExposed())
		renderNow();

}

/*

The renderNow() function calls the initialize() function when needed and if the QWindow is already initialized and prepped calls the render() method.

*/

void QTOgreWindow::renderNow()
{
	if (!isExposed())
		return;

	bool needsInitialize = false;

	if (!ogreRoot) {
		needsInitialize = true;
	}

	if (needsInitialize) {
		initialize();
	}

	render();

	if (m_animating)
		renderLater();
}

/*

Our event filter; handles the resizing of the QWindow. When the size of the QWindow changes note the call to the Ogre3D window and camera. This keeps the Ogre3D scene looking correct.

*/

bool QTOgreWindow::eventFilter(QObject *target, QEvent *event) {

	if (target == this) {

		if (event->type() == QEvent::Resize) {

			if (isExposed()) {

				ogreWindow->resize(this->width(), this->height());
				ogreCamera->setAspectRatio(Ogre::Real(ogreViewPort->getActualWidth()) / Ogre::Real(ogreViewPort->getActualHeight()));

			}
				

		}

	}

	return false;

}

/*

How we handle keyboard and mouse events.

*/

void QTOgreWindow::keyPressEvent(QKeyEvent * ev) {

	if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_W) {

		ogreCamera->setPosition(ogreCamera->getPosition() + Ogre::Vector3(0.0f, 0.0f, -1.0f));

	}

	if (ev->key() == Qt::Key_Down || ev->key() == Qt::Key_S) {

		ogreCamera->setPosition(ogreCamera->getPosition() + Ogre::Vector3(0.0f, 0.0f, 1.0f));

	}

	if (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_A) {

		ogreCamera->setPosition(ogreCamera->getPosition() + Ogre::Vector3(-1.0f, 0.0f, 0.0f));

	}

	if (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_D) {

		ogreCamera->setPosition(ogreCamera->getPosition() + Ogre::Vector3(1.0f, 0.0f, 0.0f));

	}

	if (ev->key() == Qt::Key_PageUp) {

		ogreCamera->setPosition(ogreCamera->getPosition() + Ogre::Vector3(0.0f, 1.0f, 0.0f));

	}

	if (ev->key() == Qt::Key_PageDown) {

		ogreCamera->setPosition(ogreCamera->getPosition() + Ogre::Vector3(0.0f, -1.0f, 0.0f));

	}

}

void QTOgreWindow::keyReleaseEvent(QKeyEvent * ev) {



}

/*

Function to keep track of when we should and shouldn't redraw the window; we wouldn't want to do rendering when the QWindow is minimized. This takes care of those scenarios.

*/

void QTOgreWindow::setAnimating(bool animating)
{
	m_animating = animating;

	if (animating)
		renderLater();
}
{CODE}
        

History

Information Version
Sun 19 of Apr, 2020 20:52 GMT-0000 paroj 26
Fri 25 of Nov, 2016 14:17 GMT-0000 saftschachtel 25
Thu 12 of Mar, 2015 08:02 GMT-0000 Transporter fix wheelEvent 24
Thu 12 of Mar, 2015 08:02 GMT-0000 Transporter fix wheelEvent 23
Sat 07 of Mar, 2015 16:10 GMT-0000 Transporter Improved version from the forum thread. 22
Tue 03 of Mar, 2015 00:00 GMT-0000 tmason 21
Sun 01 of Mar, 2015 22:17 GMT-0000 spacegaier added TOC and sections 20
Sun 01 of Mar, 2015 21:23 GMT-0000 tmason 19
Sun 01 of Mar, 2015 21:22 GMT-0000 tmason 18
Sun 01 of Mar, 2015 21:10 GMT-0000 tmason 17
Sun 01 of Mar, 2015 21:01 GMT-0000 tmason 16
Sun 01 of Mar, 2015 20:47 GMT-0000 tmason 15
Sun 01 of Mar, 2015 20:30 GMT-0000 tmason 14
Sun 01 of Mar, 2015 20:22 GMT-0000 tmason 13
Sun 01 of Mar, 2015 20:14 GMT-0000 tmason 12
Sun 01 of Mar, 2015 20:11 GMT-0000 tmason 11
Sun 01 of Mar, 2015 19:03 GMT-0000 tmason 10
Sun 01 of Mar, 2015 19:02 GMT-0000 tmason 9
Sun 01 of Mar, 2015 18:44 GMT-0000 tmason 8
Sun 01 of Mar, 2015 18:39 GMT-0000 tmason 7
Sun 01 of Mar, 2015 18:31 GMT-0000 tmason 6
Sun 01 of Mar, 2015 18:15 GMT-0000 tmason 5
Sun 01 of Mar, 2015 18:15 GMT-0000 tmason 4
Sun 01 of Mar, 2015 18:08 GMT-0000 tmason 3
Sun 01 of Mar, 2015 18:07 GMT-0000 tmason 2