Reputation: 281
we ’ve been looking into using transparent widgets as overlays for ogre. Although Qwidgets are transparent by default, overlaying them over an Ogre widget is different and a black screen arises. On the ogre forum (http://www.ogre3d.org/forums/viewtopic.php?f=2&t=42733) I found the following:
"Ok, to get my ideas straight I’m going to summarise the various approaches I have found to combining Ogre and Qt:
Create a window (not necessarily a QGLWidget) using Qt and pass its window handle to Ogre using the ‘externalWindowHandle’ flag. Ogre then creates a context and is fully responsible for it. This is basically what I did for the first demo I made. It has the advantage that it should support both OpenGL and Direct3D, and also should work accross all platforms. The big drawback is that Qt widgets can only be overlaid (not integrated) so you don’t get the nice transparency.
An approach similar to (1) but with the difference that the frame buffer is explicitly read back from Ogre and used as a widget background. This would again work accross all render systems and platforms, and now Qt can perform proper compositing from transparency. However, it is potenially slow doe to the need to read back every frame and copy it to Qt. But this could make a good ‘fallback’ approach. I haven’t tried implementing it yet.
This is the ‘proper’ approach which we are really considering in this thread. It involves both Ogre and Qt owning OpenGL contexts and both rendering into the same widget. This widget is set as the background of a QGraphicsView/QGraphicsScene and other widgets can be overliad with proper transpaency effects. It only works with OpenGL and is (theorectically) cross platform. There are two variations:
Allow Ogre to create it’s own OpenGL context (passing the flag ‘externalGLControl’) and then retieve the context once createRenderWindow() has finished. Under windows there are the wglGetCurrentDC() and wglGetCurrentContext() for this – other platforms should have something similar.
Have Qt create a context on Ogre’s behalf and pass that to Ogre using the ‘externalGLContext’ flag. In this case Ogre basically does nothing except render into the context it it provided with – all the control is with Qt. I haven’t quite got this to work yet as I was having crashes in both Qt and Ogre."
Approach 1 is the one we followed for a project and as said above we’ve encountered the same problem with transparency. Approach 3 is possible, but we would be limiting ourselves to openGL. The code for that is available one page 3 of the forum-link.
We’ve also found a demo video but I can’t find how it was implemented, but it looks really cool: http://youtu.be/b_3xfFuwGOQ
Upvotes: 1
Views: 2284
Reputation: 2400
I couldn't find sample implementations of options 2 and 3, so I'll share mine here. I've tested the implementations below with Qt 5.4.0 x86 and OGRE 1.10.0 x86 on Windows 10.
Disclaimer: This is sample code. I'm not recommending you to:
A possible implementation of option 3.2 is the following:
// OgreGLWidget.h
#pragma once
#include "Scene.h"
#include "OgreSceneManager.h"
#include "OgreRenderSystem.h"
#include "OgreRenderWindow.h"
#include "OgreRoot.h"
#include <QtWidgets/QOpenGLWidget>
#include <algorithm>
#include <cstdint>
class OgreGLWidget final
: public QOpenGLWidget
{
Q_OBJECT
public:
explicit OgreGLWidget(QWidget* parent = nullptr)
:
QOpenGLWidget(parent),
Root_("plugins" OGRE_BUILD_SUFFIX ".cfg", "ogre.cfg")
{ }
~OgreGLWidget() override
{
this->Scene_.clear();
if (this->SceneManager_)
this->Root_.destroySceneManager(this->SceneManager_);
if (this->RenderWindow_)
this->RenderWindow_->destroy();
}
protected:
void initializeGL() override
{
QOpenGLWidget::initializeGL();
if (auto* glRenderSystem = this->getGlRenderSystem())
{
this->Root_.setRenderSystem(glRenderSystem);
auto* renderWindow = this->Root_.initialise(false);
assert(renderWindow == nullptr && "The render window must be created afterwards");
}
else
{
throw std::runtime_error("Cannot find GL render system");
}
Ogre::NameValuePairList params;
params["externalGLControl"] = "true";
params["hidden"] = "true";
// Pass the current OpenGL context to OGRE
// - either like this
params["currentGLContext"] = "true";
// - or like this
//if (auto const* openGLContext = QOpenGLContext::currentContext())
//{
// auto const nativeHandle = openGLContext->nativeHandle();
// if (!nativeHandle.isNull() && nativeHandle.canConvert<QWGLNativeContext>())
// {
// auto nativeContext = nativeHandle.value<QWGLNativeContext>();
// auto nativeRenderingContextHandle = nativeContext.context();
// params["externalGLContext"] = Ogre::StringConverter::toString(reinterpret_cast<unsigned long>(nativeRenderingContextHandle));
// qDebug("OpenGLContext: nativeHandle=%p, windowHandle=%p", nativeRenderingContextHandle, nativeContext.window());
// }
//}
// Setup scene
this->RenderWindow_ = this->Root_.createRenderWindow("", this->width(), this->height(), false, ¶ms);
this->SceneManager_ = this->Root_.createSceneManager(Ogre::SceneType::ST_GENERIC);
this->SceneManager_->setAmbientLight(Ogre::ColourValue::White);
this->Scene_ = Scene(*this->SceneManager_, *this->RenderWindow_);
}
void paintGL() override
{
QOpenGLWidget::paintGL();
this->Root_.renderOneFrame();
// Queue another update to call paintGL again.
// Not ideal as it will render even if the scene hasn't changed.
this->update();
}
void resizeGL(int width, int height) override
{
QOpenGLWidget::resizeGL(width, height);
this->RenderWindow_->resize(width, height);
}
private:
Ogre::Root Root_;
Ogre::RenderWindow* RenderWindow_{ nullptr };
Ogre::SceneManager* SceneManager_{ nullptr };
Scene Scene_{};
Ogre::RenderSystem* getGlRenderSystem()
{
static Ogre::String const render_system_name{ "OpenGL Rendering Subsystem" };
auto const& renderSystems = this->Root_.getAvailableRenderers();
auto const lastRenderSystem = std::cend(renderSystems);
auto const glRenderSystemIt = std::find_if(std::cbegin(renderSystems), lastRenderSystem,
[](Ogre::RenderSystem const* renderSystem) { return renderSystem->getName() == render_system_name; });
return (glRenderSystemIt == lastRenderSystem) ? nullptr : *glRenderSystemIt;
}
};
Here you are a screenshot of the application running:
A possible implementation of option 2 is the following:
// OgreWidget.h
#pragma once
#include "Scene.h"
#include "OgreRoot.h"
#include "OgreRenderWindow.h"
#include "OgreSceneManager.h"
#include <QtGui/qevent.h>
#include <QtGui/QPainter>
#include <QtWidgets/QWidget>
#include <cstdint>
class OgreWidget final
: public QWidget
{
Q_OBJECT
public:
explicit OgreWidget(QWidget *parent = nullptr)
:
QWidget(parent),
Root_("plugins" OGRE_BUILD_SUFFIX ".cfg", "ogre.cfg")
{
//static Ogre::String const render_system_name{ "OpenGL Rendering Subsystem" };
static Ogre::String const render_system_name{ "Direct3D9 Rendering Subsystem" };
if (auto* renderSystem = this->getRenderSystem(render_system_name))
{
this->Root_.setRenderSystem(renderSystem);
auto* renderWindow = this->Root_.initialise(false);
assert(renderWindow == nullptr && "The render window must be created afterwards");
}
else
{
throw std::runtime_error("Cannot find render system: " + render_system_name);
}
Ogre::NameValuePairList params;
params["hidden"] = "true";
this->RenderWindow_ = this->Root_.createRenderWindow("", this->width(), this->height(), false, ¶ms);
this->SceneManager_ = this->Root_.createSceneManager(Ogre::SceneType::ST_GENERIC);
this->SceneManager_->setAmbientLight(Ogre::ColourValue::White);
this->Scene_ = Scene(*this->SceneManager_, *this->RenderWindow_);
}
~OgreWidget() override
{
this->Scene_.clear();
if (this->SceneManager_)
this->Root_.destroySceneManager(this->SceneManager_);
if (this->RenderWindow_)
this->RenderWindow_->destroy();
}
protected:
void paintEvent(QPaintEvent* evt) override
{
this->Root_.renderOneFrame();
this->RenderWindow_->update();
{
static auto const memory_category = Ogre::MemoryCategory::MEMCATEGORY_RENDERSYS;
static auto const pixel_format = Ogre::PixelFormat::PF_A8R8G8B8;
auto const width = this->RenderWindow_->getWidth();
auto const height = this->RenderWindow_->getHeight();
auto const bytesPerPixel = Ogre::PixelUtil::getNumElemBytes(pixel_format);
auto const byteCount = width * height * bytesPerPixel;
auto* data = OGRE_ALLOC_T(std::uint8_t, byteCount, memory_category);
Ogre::PixelBox pixelBox(width, height, 1, pixel_format, data);
this->RenderWindow_->copyContentsToMemory(pixelBox, pixelBox);
static auto const image_format = QImage::Format::Format_ARGB32;
QImage const background(data, width, height, image_format);
QPainter painter(this);
painter.drawImage(QPointF{ 0., 0. }, background, background.rect(), Qt::ImageConversionFlag::NoFormatConversion);
OGRE_FREE(data, memory_category);
}
QWidget::paintEvent(evt);
// Queue another update to call paintEvent again.
// Not ideal as it will render even if the scene hasn't changed.
this->update();
}
void resizeEvent(QResizeEvent* evt) override
{
auto const& size = evt->size();
auto const width = size.width();
auto const height = size.height();
this->RenderWindow_->resize(width, height);
QWidget::resizeEvent(evt);
}
private:
Ogre::Root Root_;
Ogre::RenderWindow* RenderWindow_{ nullptr };
Ogre::SceneManager* SceneManager_{ nullptr };
Scene Scene_;
Ogre::RenderSystem* getRenderSystem(Ogre::String const& renderSystemName)
{
auto const& renderSystems = this->Root_.getAvailableRenderers();
auto const lastRenderSystem = std::cend(renderSystems);
auto const renderSystemIt = std::find_if(std::cbegin(renderSystems), lastRenderSystem,
[&](Ogre::RenderSystem const* renderSystem) { return renderSystem->getName() == renderSystemName; });
return (renderSystemIt == lastRenderSystem) ? nullptr : *renderSystemIt;
}
};
Here you are a screenshot of the application running:
This approach is considerably slower than the OpenGL one. On my machine, when the application is maximized, a frame is rendered in about:
For some reason, when using the buffered OpenGL backend, the cube has a different color:
I've not bothered to investigate it: if OpenGL is available, you most probably want to render the scene with class OgreGLWidget
.
I'm embedding the OGRE scene as a child widget of centralWidget
in my MainWindow
instance:
// MainWindow.h
#pragma once
#include "OgreGLWidget.h"
#include "OgreWidget.h"
#include <QtWidgets/QLabel>
#include <QtWidgets/QLayout>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QPushButton>
#include "ui_MainWindow.h"
class MainWindow final
: public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr)
: QMainWindow(parent)
{
this->Ui_.setupUi(this);
auto* gridLayout = new QGridLayout(this->Ui_.centralWidget);
gridLayout->setSpacing(6);
gridLayout->setContentsMargins(0, 0, 0, 0);
// Uncomment based on whether you want to use OpenGL or D3D9 as backend.
this->OgreWidget_ = new OgreGLWidget(this->Ui_.centralWidget);
//this->OgreWidget_ = new OgreWidget(this->Ui_.centralWidget);
gridLayout->addWidget(this->OgreWidget_);
// Show an overlay widget when "Show Widget" is pressed.
auto* action = new QAction("Show Widget", this);
QObject::connect(action, &QAction::triggered, this, &MainWindow::showWidgetSlot);
this->Ui_.mainToolBar->addAction(action);
}
~MainWindow() override = default;
private slots:
void showWidgetSlot()
{
auto* w = new QWidget(this->OgreWidget_);
w->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
w->setAttribute(Qt::WidgetAttribute::WA_NoMousePropagation);
w->setStyleSheet(".QWidget { background-color: rgba(255,255,255,75%); border-radius: 10px; }");
auto layout = new QVBoxLayout(w);
layout->addWidget(new QLabel(this->tr("Hello OGRE"), w));
auto* b = new QPushButton(this->tr("Ok"), w);
layout->addWidget(b);
QObject::connect(b, &QPushButton::clicked, w, &QWidget::close);
w->resize(100, 100);
w->show();
}
private:
Ui::MainWindowClass Ui_;
QWidget* OgreWidget_{ nullptr };
};
Class Scene
, responsible for setting up my scene, is implemented like this:
// Scene.h
#pragma once
#include "OgreCamera.h"
#include "OgreEntity.h"
#include "OgreMaterialManager.h"
#include "OgrePass.h"
#include "OgreRenderTarget.h"
#include "OgreSceneManager.h"
#include "OgreSceneNode.h"
#include "OgreTechnique.h"
#include "OgreViewport.h"
struct Scene
{
explicit Scene() = default;
explicit Scene(Ogre::SceneManager& sceneManager_, Ogre::RenderTarget& renderTarget_)
: sceneManager(&sceneManager_)
{
auto cubeMaterial = Ogre::MaterialManager::getSingleton().create("cubeMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
if (auto* technique = cubeMaterial->getTechnique(0))
{
if (auto* pass = technique->getPass(0))
{
pass->setLightingEnabled(true);
pass->setAmbient(Ogre::ColourValue{ 0.3f, 0.1f, 0.1f, 1 });
}
}
this->entity = sceneManager_.createEntity(Ogre::SceneManager::PrefabType::PT_CUBE);
this->entity->setMaterial(cubeMaterial);
sceneManager_.getRootSceneNode()->attachObject(this->entity);
this->camera = sceneManager_.createCamera("camera");
this->camera->setPosition(-100, 100, 300);
this->camera->lookAt(0, 0, 0);
this->camera->setNearClipDistance(5);
this->light = sceneManager_.createLight("light");
this->light->setType(Ogre::Light::LightTypes::LT_DIRECTIONAL);
this->light->setDirection(0.25, -1, -0.5);
this->viewport = renderTarget_.addViewport(this->camera);
this->viewport->setBackgroundColour(Ogre::ColourValue::Blue);
}
Scene(Scene const&) = delete;
Scene(Scene&& other)
{
this->swap(other);
}
Scene& operator=(Scene const&) = delete;
Scene& operator=(Scene&& other)
{
this->swap(other);
return *this;
}
void swap(Scene& other)
{
using std::swap;
swap(this->sceneManager, other.sceneManager);
swap(this->viewport, other.viewport);
swap(this->entity, other.entity);
swap(this->camera, other.camera);
swap(this->light, other.light);
}
void clear()
{
if (this->light)
{
this->sceneManager->destroyLight(this->light);
this->light = nullptr;
}
if (this->camera)
{
this->sceneManager->destroyCamera(this->camera);
this->camera = nullptr;
}
if (this->entity)
{
this->sceneManager->destroyEntity(this->entity);
this->entity = nullptr;
}
this->sceneManager = nullptr;
}
~Scene()
{
this->clear();
}
// -- Non-owning
Ogre::SceneManager* sceneManager{ nullptr };
Ogre::Viewport* viewport{ nullptr };
// -- Owning
Ogre::Entity* entity{ nullptr };
Ogre::Camera* camera{ nullptr };
Ogre::Light* light{ nullptr };
};
Upvotes: 2
Reputation:
You can now find an implementation of the second variant of your third proposed approach in an updated QmlOgre example at https://github.com/advancingu/QmlOgre The original example had a few issues that were fixed here.
Hope this still helps a few months after your question.
Upvotes: 2
Reputation: 281
*Here is the code for that video (http://youtu.be/b_3xfFuwGOQ). (Needs Qt5) http://qt.gitorious.org/qt-labs/qmlogre
There is also Cutexture (Needs Qt4) https://github.com/advancingu/Cutexture http://i56.tinypic.com/b6xb83.jpg
Here is a Qt4 Ogre Widget example. (Needs Qt4) http://zester.googlecode.com/files/QtCreatorOgre2.zip*
Upvotes: 0