Skip to main content

History: Integrating Ogre into QT5

Source of version: 15

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

	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()
{
	
	Ogre::WindowEventUtilities::messagePump();
	ogreRoot->renderOneFrame();

}

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

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

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

	if (isExposed())
		renderNow();

}

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

	bool needsInitialize = false;

	if (!ogreRoot) {
		needsInitialize = true;
	}

	if (needsInitialize) {
		initialize();
	}

	render();

	if (m_animating)
		renderLater();
}

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;

}

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



}

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