[osg-users] OSG 3.2.1 and Qt5 Widget integration
Trajce Nikolov NICK
trajce.nikolov.nick at gmail.com
Mon Aug 3 07:20:34 PDT 2015
Hi Sebastian,
I am not using Qt but faced the same problem. So here is a 'hack' for
Windows if it helps:
osgViewer::CompositeViewer::Windows wins;
viewer->getWindows(wins);
while (!viewer->done())
{
#if defined(_WIN32)
MSG msg;
if (::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
::GetMessage(&msg, NULL, 0, 0);
if (wins.size())
{
osgViewer::GraphicsHandleWin32 *hdl =
dynamic_cast<osgViewer::GraphicsHandleWin32*>(wins.at(0));
if(hdl)
{
WNDPROC fWndProc =
(WNDPROC)::GetWindowLongPtr(hdl->getHWND(), GWLP_WNDPROC);
if (fWndProc && hdl->getHWND())
{
::CallWindowProc(fWndProc,hdl->getHWND(),msg.message, msg.wParam,
msg.lParam);
}
}
}
}
#endif
On Mon, Aug 3, 2015 at 3:43 PM, Sebastian Messerschmidt <
sebastian.messerschmidt at gmx.de> wrote:
> Hi Can,
>
> Have you created a full code example yet?
> My problem right now is the lack of keyboard events being passed through
> to OSG. Any hints on this?
>
> Cheers
> Sebastian
>
> Hi,
>>
>> I have been working Qt5 integration for my current rendering application
>> implementing deferred rendering and came up with couple of solutions. I
>> want to share it with the people struggling Qt5 integration while waiting
>> official stable release :)
>>
>> Since the current stable release is OSG 3.2.1, this will be based on that
>> version.
>>
>> For Qt5 version, I recommend using >= 5.4, because in earlier versions
>> you have to do a lot by yourself. In 5.4, at least you have QOpenGLWidget.
>>
>> Even though I will give solution for widget, this can be applied to
>> QWindow solution as well. The codes will be bits and pieces, unfortunately
>> cannot share full working code.
>>
>> Firstly, create a new widget rendering class subclassing QOpenGLWidget.
>> This one is almost same as the QGLWidget version of it.
>>
>>
>> Code:
>>
>> class RenderWidget : public [b]QOpenGLWidget[/b]
>> {
>> Q_OBJECT
>> public:
>> RenderWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);
>> ~RenderWidget();
>>
>> protected:
>> virtual void initializeGL();
>> virtual void paintGL();
>> virtual void resizeGL(int width, int height);
>>
>> osg::ref_ptr<osgViewer::GraphicsWindow> gw;
>> osg::ref_ptr<osgViewer::Viewer> viewer;
>>
>> private:
>> QTimer heartbeat;
>> };
>>
>> RenderWidget::RenderWidget(QWidget* parent, Qt::WindowFlags f)
>> {
>> // instead of osgViewer::setUpViewerAsEmbeddedInWindow, we are going
>> to
>> // inject our osg::State subclass
>> gw = new GraphicsWindowEx(0, 0, width(), height());
>> viewer = new osgViewer::Viewer();
>> viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
>> // setup viewer's camera etc.
>> // In my case, I don't want the base camera to clear anything
>> // I have a lot of other cameras queued as FBO rendering
>> viewer->getCamera()->setViewport(0, 0, width(), height())
>> viewer->getCamera()->setGraphicsContext(gw);
>> viewer->getCamera()->setClearMask(0);
>> //...
>> connect(&heartbeat, SIGNAL(timeout()), this, SLOT(update()),
>> Qt::QueuedConnection);
>> hearbeat.start(10);
>> }
>>
>> void RenderWidget::initializeGL()
>> {
>> viewer->realize();
>> }
>>
>> void RenderWidget::paintGL()
>> {
>> static_cast<StateEx
>> *>(&state)->setDefaultFbo(defaultFramebufferObject());
>>
>> viewer->frame();
>> // OR if you want to mix OSG with Qt 2D API
>> QPainter painter(this);
>> painter.beginNativePainting();
>> viewer->frame();
>> painter.endNativePainting();
>> // calculate fps...
>> painter.setPen(Qt::white);
>> painter.drawText(width() - 100, 10, 50, 25, Qt::AlignLeft,
>> QString::number(fps));
>> painter.end();
>> }
>>
>> void RenderWidget::resizeGL(int width, int height)
>> {
>> gw->getEventQueue()->windowResize(0, 0, width, height);
>> gw->resized(0, 0, width, height);
>> //...
>> }
>>
>>
>>
>>
>> The difference between old QGLWidget and QOpenGLWidget is how they handle
>> the rendering in the background. QOpenGLWidget is using QOffscreenSurface
>> and QFrameBufferObject to render its content. The main problem of the
>> current OSG integration is that it does not expect a superior FBO as main
>> framebuffer. Like in my case, if you are using a lot of FBOs, some point
>> OSG unbinds them and returns to direct drawing or leaves the last FBO bound
>> after drawing. However, it should return(bind) to our superior FBO used by
>> QOpenGLWidget.
>>
>> Let me explain it with the source code of OSG.
>>
>>
>> Code:
>>
>> void RenderStage::drawInner(osg::RenderInfo& renderInfo,
>> osgUtil::RenderLeaf*& previous, bool& doCopyTexture)
>> {
>> //...
>> osg::State& state = *renderInfo.getState();
>>
>> osg::FBOExtensions* fbo_ext = _fbo.valid() ?
>> osg::FBOExtensions::instance(state.getContextID(),true) : 0;
>> bool fbo_supported = fbo_ext && fbo_ext->isSupported();
>>
>> bool using_multiple_render_targets = fbo_supported &&
>> _fbo->hasMultipleRenderingTargets();
>>
>> if (!using_multiple_render_targets)
>> {
>> #if !defined(OSG_GLES1_AVAILABLE) &&
>> !defined(OSG_GLES2_AVAILABLE)
>>
>> if( getDrawBufferApplyMask() )
>> glDrawBuffer(_drawBuffer);
>>
>> if( getReadBufferApplyMask() )
>> glReadBuffer(_readBuffer);
>>
>> #endif
>> }
>>
>> if (fbo_supported)
>> {
>> _fbo->apply(state);
>> }
>> RenderBin::draw(renderInfo,previous);
>> //...
>> }
>>
>>
>>
>>
>> As you can see, _fbo->apply(state); is the only point where FBO of the
>> camera (which comes from our osg::Camera and RenderStage::runCameraSetUp)
>> is bound before drawing our geometry etc. However, there is no line to
>> handle returning back to FBO of QOpenGLWidget. Even we put a empty FBO as a
>> last camera, it will executes following line:
>>
>>
>> Code:
>>
>> void FrameBufferObject::apply(State &state, BindTarget target) const
>> {
>> //...
>> if (_attachments.empty())
>> {
>> ext->glBindFramebuffer(target, 0);
>> return;
>> }
>> //...
>> }
>>
>>
>>
>>
>> So basicly, it switches back to direct rendering because of 0 argument.
>> Therefore, we have to inject default FBO of QOpenGLWidget somehow. You
>> might already notice two special lines in paintGL() and constructor methods
>> of RenderWidget implementation. We are going to subclass osg::State and
>> osgViewer::GraphicsWindow. Here are the classes:
>>
>>
>> Code:
>>
>>
>> class StateEx : public osg::State
>> {
>> public:
>> StateEx();
>>
>> inline void setDefaultFbo(GLuint fbo);
>> inline GLuint getDefaultFbo() const;
>>
>> protected:
>> GLuint defaultFbo;
>> };
>>
>> StateEx::StateEx() :
>> defaultFbo(0)
>> {
>> }
>>
>> inline void StateEx::setDefaultFbo(GLuint fbo)
>> {
>> defaultFbo = fbo;
>> }
>>
>> inline GLuint getDefaultFbo() const
>> {
>> return defaultFbo;
>> }
>>
>>
>>
>>
>> Code:
>>
>> class GraphicsWindowEx : public osgViewer::GraphicsWindow
>> {
>> public:
>> GraphicsWindowEx(osg::GraphicsContext::Traits* traits);
>> GraphicsWindowEx(int x, int y, int width, int height);
>>
>> void init();
>>
>> virtual bool isSameKindAs(const osg::Object* object) const { return
>> dynamic_cast<const GraphicsWindowEx *>(object) != 0; }
>> virtual const char* libraryName() const { return ""; }
>> virtual const char* className() const { return "GraphicsWindowEx"; }
>>
>> // dummy implementations, assume that graphics context is *always*
>> current and valid.
>> virtual bool valid() const { return true; }
>> virtual bool realizeImplementation() { return true; }
>> virtual bool isRealizedImplementation() const { return true; }
>> virtual void closeImplementation() {}
>> virtual bool makeCurrentImplementation() { return true; }
>> virtual bool releaseContextImplementation() { return true; }
>> virtual void swapBuffersImplementation() {}
>> virtual void grabFocus() {}
>> virtual void grabFocusIfPointerInWindow() {}
>> virtual void raiseWindow() {}
>> };
>>
>> GraphicsWindowEx::GraphicsWindowEx(osg::GraphicsContext::Traits* traits)
>> {
>> _traits = traits;
>> init();
>> }
>>
>> GraphicsWindowEx::GraphicsWindowEx(int x, int y, int width, int height);
>> {
>> _traits = new osg::GraphicsContext::Traits();
>> _traits->x = x;
>> _traits->x = y;
>> _traits->width = width;
>> _traits->height = height;
>>
>> init();
>> }
>>
>> void GraphicsWindowEx::init()
>> {
>> if(valid())
>> {
>> // inject our "extended" state
>> setState(new StateEx());
>> getState()->setGraphicsContext(this);
>> if (_traits.valid() && _traits->sharedContext.valid())
>> {
>>
>> getState()->setContextID(_traits->sharedContext->getState()->getContextID()
>> );
>> incrementContextIDUsageCount(getState()->getContextID());
>> }
>> else
>> {
>>
>> getState()->setContextID(osg::GraphicsContext::createNewContextID());
>> }
>> }
>> }
>>
>>
>>
>>
>> Now, our rendering knows the default FBO of the QOpenGLWidget so with
>> custom RenderStage we can use this information. Only method we have to
>> override is drawInner.
>>
>>
>> Code:
>>
>> void RenderStageEx::drawInner(osg::RenderInfo& renderInfo,
>> osgUtil::RenderLeaf*& previous, bool& doCopyTexture)
>> {
>> //...
>> osg::State& state = *renderInfo.getState();
>> osg::FBOExtensions* fbo_ext =
>> osg::FBOExtensions::instance(state.getContextID(), true);
>> bool fbo_supported = fbo_ext && fbo_ext->isSupported();
>> if (fbo_supported)
>> {
>> if(_fbo.valid())
>> {
>> if (!_fbo->hasMultipleRenderingTargets())
>> {
>> #if !defined(OSG_GLES1_AVAILABLE) &&
>> !defined(OSG_GLES2_AVAILABLE)
>>
>> if( getDrawBufferApplyMask() )
>> glDrawBuffer(_drawBuffer);
>>
>> if( getReadBufferApplyMask() )
>> glReadBuffer(_readBuffer);
>>
>> #endif
>> }
>>
>> _fbo->apply(state);
>> }
>> else
>>
>> fbo_ext->glBindFramebuffer(osg::FrameBufferObject::READ_DRAW_FRAMEBUFFER,
>> static_cast<StateEx *>(&state)->getDefaultFbo());
>> }
>> RenderBin::draw(renderInfo,previous);
>> //...
>> }
>>
>>
>>
>>
>> After all this hussle, the most strange part of this solutions is
>> extending osgUtil::CullVisitor. Unfortunately, there is no easy way to
>> inject our RenderStageEx into the rendering pipeline. To solve it, I have
>> overriden the apply(osg::Camera& camera) method. Actually almost all the
>> code come from the original source code but two special care should be
>> given. Firstly, here is what I did:
>>
>>
>> Code:
>>
>>
>> void CullVisitorEx::apply(osg::Camera &camera)
>> {
>> //...
>> if(camera.getRenderOrder() == osg::Camera::NESTED_RENDER)
>> {
>> handle_cull_callbacks_and_traverse(camera);
>> }
>> else
>> {
>> osgUtil::RenderStage *prevRenderStage =
>> getCurrentRenderBin()->getStage();
>> osg::ref_ptr<RenderStageCacheEx> rsCache =
>> dynamic_cast<RenderStageCacheEx *>(camera.getRenderingCache());
>> if(!rsCache)
>> {
>> rsCache = new RenderStageCacheEx();
>> camera.setRenderingCache(rsCache);
>> }
>>
>> osg::ref_ptr<osgUtil::RenderStage> rtts =
>> rsCache->getRenderStage(this);
>> if(!rtts)
>> {
>> rtts = new RenderStageEx();
>>
>> //...
>> }
>> else
>> rtts->reset();
>>
>> //...
>> }
>>
>> //...
>> }
>>
>>
>>
>>
>> I kept only the parts that changed. Instead of new
>> osgUtil::RenderStage(), we should call new RenderStageEx(). Unfortunately,
>> renderstage caching implementation is hidden (implemented inside CPP) so
>> you have to create your own RenderStageCache from scratch. Just copy the
>> source code of it into the beginning of your CullVisitorEx.cpp and rename
>> it.
>>
>> If you can manage to properly inject all these classes into your project,
>> you can use any combination of FBO rendering with Qt5 and OSG 3.2.1. I have
>> struggled a lot to make it work in current stable release. Hope this helps.
>>
>> P.S. I am preparing full source code and post it later.
>>
>> Thank you!
>>
>> Happy coding,
>> Can[/code]
>>
>> ------------------
>> Read this topic online here:
>> http://forum.openscenegraph.org/viewtopic.php?p=64403#64403
>>
>>
>>
>>
>>
>> _______________________________________________
>> osg-users mailing list
>> osg-users at lists.openscenegraph.org
>> http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
>>
>
> _______________________________________________
> osg-users mailing list
> osg-users at lists.openscenegraph.org
> http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
>
--
trajce nikolov nick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openscenegraph.org/pipermail/osg-users-openscenegraph.org/attachments/20150803/b92e5c5b/attachment-0002.htm>
More information about the osg-users
mailing list