[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