Render into a QQuikItem

Hello,

Anybody any pointers on how to render into a QQuickItem? So far I am able to render into a qquickitem by using a

QQuickFramebufferObject derived class and creating a renderer. This works but always renders in the complete window

instead of only the QQuickItem contents. I tried with deriving my own window class from Aspect_Window but that didn't help.

What is the best way of integrating OCC with qml?

Thanks,

Arjan

 

 

Frank Martinez's picture

I know this is an old post, but I have the same problem in 2020. I have tried lots of examples from github and other sources. My QQuickFramebufferObject has two problems:

1. it renders on complete window

2. It is flickering on resize.

This is the code:

        //================================================================
        // Item
        //================================================================
        
        OcctViewItem::OcctViewItem(QQuickItem *parent) : QQuickFramebufferObject(parent)
        {
            setMirrorVertically(true);
        }

        OcctViewItem::~OcctViewItem() {
        }
        
        QQuickFramebufferObject::Renderer * OcctViewItem::createRenderer() const
        {
            return new OcctFboRenderer();
        }
        
        //================================================================
        // Renderer
        //================================================================

        OcctFboRenderer::OcctFboRenderer() { 
        }

        OcctFboRenderer::~OcctFboRenderer()
        {
            context_.Nullify();
            view_.Nullify();
            viewer_.Nullify();
            window_ = nullptr;
        }

        void OcctFboRenderer::render()
        {
            QOpenGLContext::currentContext()->functions()->glUseProgram(0);
            if (!view_.IsNull()) {
                view_->Redraw();
                view_->MustBeResized();               
            }
            window_->resetOpenGLState();
        }

        QOpenGLFramebufferObject * OcctFboRenderer::createFramebufferObject(const QSize &size)
        {
            QOpenGLFramebufferObjectFormat format;
            //format.setSamples(4);
            format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
            return new QOpenGLFramebufferObject(size, format);
        }

        void OcctFboRenderer::synchronize(QQuickFramebufferObject * item)
        {
            if (!window_) {
                window_ = item->window();
                window_->setClearBeforeRendering(Standard_False);
            }
            OcctViewItem * self = static_cast<OcctViewItem*> (item);

            // If the viewer is not yet initialized, initialize it.
            if (viewer_.IsNull()) {
                this->initializeViewer(Aspect_Drawable(window_->winId()), self);
            }

            // Get the control position and size.
            QPoint viewportPos = self->mapToGlobal(QPointF(0, 0)).toPoint();
            QSize viewportSize = self->size().toSize();

            // Check if the viewPort needs to be resized.
            if (viewportPos.x() != viewportPos_.x() || viewportPos.y() != viewportPos_.y()) {
                view_->MustBeResized();
            }
            if (viewportSize.width() != viewportSize_.width() || viewportSize.height() != viewportSize_.height()) {
                view_->MustBeResized();
                view_->Invalidate();
            }

            // Store the current pos and size.
            viewportPos_ = viewportPos;
            viewportSize_ = viewportSize;

        }

        void OcctFboRenderer::initializeViewer(const Aspect_Drawable& drawable, OcctViewItem * occView)
        {
            Q_ASSERT(viewer_.IsNull());

            // Request device and render context.
            auto renderContext = window_->openglContext();
            if (drawable == 0 || renderContext == 0)
                return;

            // Setup display driver.
            Handle(Aspect_DisplayConnection) display = new Aspect_DisplayConnection();
            Handle(OpenGl_GraphicDriver) driver = new OpenGl_GraphicDriver(display, Standard_False);

            // Create and setup the viewer.
            viewer_ = new V3d_Viewer(driver);
            viewer_->SetDefaultBackgroundColor(Quantity_NOC_GRAY50);
            viewer_->SetDefaultLights();
            viewer_->SetLightOn();

            // Create and setup interactivity context.
            context_ = new AIS_InteractiveContext(viewer_);
            context_->SetDisplayMode(AIS_Shaded, Standard_True);

            // Create and setup window.
            Handle(OcctWindow) occtWnd = new OcctWindow(window_);
            if (!occtWnd->IsMapped()) {
                occtWnd->Map();
            }

            // Create and setup view.
            view_ = viewer_->CreateView();
            view_->ChangeRenderingParams().NbMsaaSamples = 4;
            view_->ChangeRenderingParams().IsAntialiasingEnabled = Standard_True;
            view_->SetImmediateUpdate(Standard_False);
            //view_->SetWindow(occtWnd, reinterpret_cast<Aspect_RenderingContext>(renderContext));
            view_->SetWindow(occtWnd);
            
            
            view_->TriedronDisplay(Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITESMOKE, 0.1, V3d_ZBUFFER);

            // Create a demo scene.
            this->initScene();

        }

               
        void OcctFboRenderer::initScene()
        {
            // Create a bisque cone at [0, 10, 0].
            gp_Ax2 axis;
            axis.SetLocation(gp_Pnt(0.0, 10.0, 0.0));

            TopoDS_Shape bisqueCone = BRepPrimAPI_MakeCone(axis, 3.0, 1.5, 5.0).Shape();
            Handle(AIS_Shape) bisqueConeShape = new AIS_Shape(bisqueCone);
            bisqueConeShape->SetColor(Quantity_NOC_BISQUE);

            // Create a chocolate cone at [8, 10, 0].
            axis.SetLocation(gp_Pnt(8.0, 10.0, 0.0));
            TopoDS_Shape chocoCone = BRepPrimAPI_MakeCone(axis, 3.0, 0.0, 5.0).Shape();
            Handle(AIS_Shape) chocoConeShape = new AIS_Shape(chocoCone);
            chocoConeShape->SetColor(Quantity_NOC_CHOCOLATE);

            context_->Display(bisqueConeShape, Standard_True);
            context_->Display(chocoConeShape, Standard_True);

            // Fit all into the view.
            view_->FitAll();


        }

- First render is black screen: screenshot.png

https://www.opencascade.com/system/files/forum/screenshot.png

- While resizing, flick between screenshot_1.png and screenshot_2.png

https://www.opencascade.com/system/files/forum/screenshot_1.png

https://www.opencascade.com/system/files/forum/screenshot_2.png

Thank you for your help,

Frank.

Kirill Gavrilov's picture

OCCT 3D Viewer has its own offscreen framebuffer (FBO) management and by default renders into the window.

If you want to render into Qt framebuffer (QOpenGLFramebufferObject), then you should wrap it into OpenGl_FrameBuffer (there are auxiliary methods like  OpenGl_FrameBuffer::InitWrapper()) or in opposite direction (create an OCCT framebuffer and wrap it into Qt, if there is such an API in Qt)  and then assign it as default framebuffer to OCCT 3D Viewer OpenGl_Context::SetDefaultFrameBuffer()) before redraw (V3d_View::Redraw()) and after each resize. This would target OCCT 3D Viewer to render into this offscreen FBO instead of a window. Of course, you will need also disabling buffer swap by OCCT 3D Viewer (OpenGl_Caps::buffersNoSwap=true) and ensure redraw is done only within specific Qt method (disable immediate updates V3d_View::SetImmediateUpdate(false) and weed out application code from incorrect places of viewer update), not at arbitrary time - like it is done in QtQuick sample in OCCT, for example.

Kael Zhou's picture

Hi, Kirill

I am trying to make OCCT render into QQuickFramebufferObject following your instruction. But I wonder how can I make OCCT use the opengl context I get in QQuickFramebufferObject::Renderer::render() function:
opencascade::handle<OpenGl_Context> occtCtx = new OpenGl_Context();
ok = occtCtx->Init(); //Initialize class from currently bound OpenGL context
qDebug() << "occtCtx Init ok: " << ok; //True
opencascade::handle<OpenGl_FrameBuffer> occtFbo = new OpenGl_FrameBuffer();
ok = occtFbo->InitWrapper(occtCtx); //Initialize class from currently bound FBO.
qDebug() << "occtFbo InitWrapper ok: " << ok; //True
qDebug() <<occtFbo->GetVPSizeX() << occtFbo->GetVPSizeY(); //Same as QQuickFramebufferObject.size()
//TODO: how to make OCCT render using occtCtx

BTW, I‘m using opencascade v7.4.0.

Frank Martinez's picture

Thank you Kirill,

I already solved the problem, I was experimenting with QQuickFramebufferObject and didn't understand it very well at first. After I Implemented an Aspect_Window and separate rendering code from the GUI thread, everything started to work very well.

Cheers!

Kirill Gavrilov's picture

Good to know ;).
 

windtail's picture

Hi Frank,

Would you please show a minimal example ? I need render into a QQuikItem too.

Thanks!