Adrian Maire
Adrian Maire

Reputation: 14865

How to visualize OpenCascade on QOpenGLWidget or QQuickFramebufferObject (qt6)?

I had it working with QGLWidget (following this page) in older version of Qt, but I am stuck attempting to update to Qt6.

Additionally, I would like a good integration, to move to QQuickFramebufferObject and QtQuick2.

Attempt 1 not fully working

Attempt 1:

This attempt to create a WNT_Window(on Windows) by using QWidget handle. It "seems" to work when I set Qt::WA_PaintOnScreen, however the standard output shows many messages:

QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1

Removing Qt::WA_PaintOnScreen, the image blinks like if OCCT was drawing directly on the screen and Qt swapping buffers after.

    Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);

    // OCC Viewer
    m_viewer = new V3d_Viewer(graphicDriver);

    // OCC View
    m_view = m_viewer->CreateView();

    // Make this QOpenGLWindow to be the surface for the view
    WId windowHandle = (WId)winId();

    Handle(Aspect_Window) wind;
    #ifdef WIN32
        wind = new WNT_Window((Aspect_Handle)windowHandle);
    #elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
        wind = new Cocoa_Window((NSView *)windowHandle);
    #else
        wind = new Xw_Window(aDisplayConnection, (Window)windowHandle);
    #endif

    m_view->SetWindow(wind);
    if (!wind->IsMapped()) wind->Map();

    // OCC Context
    m_context = new AIS_InteractiveContext(m_viewer);

Attempt 2:

This strategy creates an Aspect_NeutralWindow initialized from the QWidget handle and the current GL context.

This solution is unable to show any OCCT content.

    // Create OpenGL graphic driver
    Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);

    // OCC Viewer
    m_viewer = new V3d_Viewer(graphicDriver);

    // OCC View
    m_view = m_viewer->CreateView();


    // Aspect rendering context should use the OpenGL context of QOpenGLWidget
    Aspect_RenderingContext renderContext = (Aspect_RenderingContext)QOpenGLContext::currentContext()->handle();
    Handle(Aspect_Window) wind = new Aspect_NeutralWindow();

    m_view->SetWindow(wind, renderContext);
    if (!wind->IsMapped()) wind->Map();

    // OCC Context
    m_context = new AIS_InteractiveContext(m_viewer);

Attempt 3:

This strategy, following this page, attempt to use GL context and build OCCT to use it.

    // Create OpenGL graphic driver
    Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);

    Handle(OpenGl_Context) oglContext = new OpenGl_Context();
    oglContext->Init(QOpenGLContext::currentContext()); //Initialize from currently bound OpenGL context
    Handle(OpenGl_FrameBuffer) fbo = new OpenGl_FrameBuffer();
    fbo->InitWrapper(oglContext);
    oglContext->SetDefaultFrameBuffer(fbo);


    // OCC Viewer
    m_viewer = new V3d_Viewer(graphicDriver);

    // OCC View
    m_view = m_internal->m_viewer->CreateView();
    m_view->SetImmediateUpdate(false);
    graphicDriver->ChangeOptions().buffersNoSwap = false;

    // OCC Context
    m_context = new AIS_InteractiveContext(m_viewer);

Again, OCCT is unable to show any content.


Full example:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(VisiTouchPSL LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)

find_package(OpenCASCADE REQUIRED)

set(EXEC_NAME TestOcctQt6)

# Find Qt packages
find_package(Qt6 COMPONENTS Widgets OpenGLWidgets REQUIRED)

# Add your source and headers files here
set(HEADERS
    occView.h
)
set(SOURCES
    occView.cpp
    main.cpp
)

# Create executable
add_executable(${EXEC_NAME} ${HEADERS} ${SOURCES})

# Link Qt libraries
target_link_libraries(${EXEC_NAME} PRIVATE Qt6::Widgets Qt6::OpenGLWidgets ${OpenCASCADE_LIBRARIES})

# Include OpenCASCADE headers
target_include_directories(${EXEC_NAME} PRIVATE ${OpenCASCADE_INCLUDE_DIRS})

// main.cpp
// Author ADRIAN MAIRE
// MIT license

#include "occView.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    OcctViewer w;
    w.resize(800, 600);
    w.show();

    return a.exec();
}

//occView.h
// Author ADRIAN MAIRE
// MIT license

#ifndef OCCTVIEWER_H
#define OCCTVIEWER_H

#include <memory>

#include <QOpenGLWidget>

#include <AIS_InteractiveContext.hxx>
#include <Standard_Handle.hxx>

//! Adapted a QWidget for OpenCASCADE viewer.
class OcctViewer : public QOpenGLWidget
{
    Q_OBJECT

    // Pimpl to not expose OCCL stuff used internally
    struct Internal;

public:
    explicit OcctViewer(QWidget* parent=nullptr);
    ~OcctViewer() override;

    // Provide access to OCCT context, for building the scene
    const Handle(AIS_InteractiveContext)& getContext() const;

protected:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int h) override;

    // Some default content for this example.
    void makeTorus();

    std::unique_ptr<Internal> m_internal;
public slots:
    void rotateScene();


};

#endif

// occView.cpp
// Author ADRIAN MAIRE
// MIT license

#include "occView.h"

#include <QOpenGLContext>
#include <QTimer>

#include <OpenGl_GraphicDriver.hxx>

#include <V3d_View.hxx>

#include <Aspect_Handle.hxx>
#include <Aspect_NeutralWindow.hxx>
#include <Aspect_DisplayConnection.hxx>
#include <OpenGl_FrameBuffer.hxx>
#include <OpenGl_Context.hxx>

#include <WNT_Window.hxx>

#include <BRepPrimAPI_MakeTorus.hxx>
#include <AIS_Shape.hxx>

#include <OpenGl_GraphicDriver.hxx>
#include <QOpenGLFunctions>


struct OcctViewer::Internal
{
    Handle(V3d_Viewer) m_viewer;
    Handle(V3d_View) m_view;
    Handle(AIS_InteractiveContext) m_context;

    //For texting/example
    Handle(AIS_Shape) m_torusShape;
    double m_rotationAngle = 0.0;
    QTimer timer;
};

OcctViewer::OcctViewer(QWidget* parent )
    : QOpenGLWidget(parent)
    , m_internal(std::make_unique<Internal>())
{
    setAttribute(Qt::WA_PaintOnScreen);

    setAttribute(Qt::WA_NoSystemBackground); // Avoid many QPainter prints/errors
    setAutoFillBackground(false);
    setUpdateBehavior(QOpenGLWidget::NoPartialUpdate);

    // Setup the rotation of the scene
    connect(&m_internal->timer, &QTimer::timeout, this, &OcctViewer::rotateScene);
    m_internal->timer.setInterval(500);
    m_internal->timer.start();
}

OcctViewer::~OcctViewer() = default;


void OcctViewer::initializeGL()
{
    QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions();

    //ATTEMPT 3
    /*// Create OpenGL graphic driver
    Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);

    Handle(OpenGl_Context) oglContext = new OpenGl_Context();
    oglContext->Init(QOpenGLContext::currentContext()); //Initialize from currently bound OpenGL context
    Handle(OpenGl_FrameBuffer) fbo = new OpenGl_FrameBuffer();
    fbo->InitWrapper(oglContext);
    oglContext->SetDefaultFrameBuffer(fbo);


    // OCC Viewer
    m_internal->m_viewer = new V3d_Viewer(graphicDriver);

    // OCC View
    m_internal->m_view = m_internal->m_viewer->CreateView();
    m_internal->m_view->SetImmediateUpdate(false);
    graphicDriver->ChangeOptions().buffersNoSwap = false;*/


    // ATTEMPT 2
    /*
    // Create OpenGL graphic driver
    Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);

    // OCC Viewer
    m_internal->m_viewer = new V3d_Viewer(graphicDriver);

    // OCC View
    m_internal->m_view = m_internal->m_viewer->CreateView();


    // Aspect rendering context should use the OpenGL context of QOpenGLWidget
    Aspect_RenderingContext renderContext = (Aspect_RenderingContext)QOpenGLContext::currentContext()->handle();
    Handle(Aspect_Window) wind = new Aspect_NeutralWindow();

    m_internal->m_view->SetWindow(wind, renderContext);
    if (!wind->IsMapped()) wind->Map();*/


    //ATTEMPT 1

    // Create OpenGL graphic driver
    /*Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);

    // OCC Viewer
    m_internal->m_viewer = new V3d_Viewer(graphicDriver);

    // OCC View
    m_internal->m_view = m_internal->m_viewer->CreateView();

    // Make this QOpenGLWindow to be the surface for the view
    WId windowHandle = (WId)winId();

    Handle(Aspect_Window) wind;
    #ifdef _WIN32
        wind = new WNT_Window((Aspect_Handle)windowHandle);
    #elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
        wind = new Cocoa_Window((NSView *)windowHandle);
    #else
        wind = new Xw_Window(aDisplayConnection, (Window)windowHandle);
    #endif

    m_internal->m_view->SetWindow(wind);
    if (!wind->IsMapped()) wind->Map();*/

    // OCC Context
    m_internal->m_context = new AIS_InteractiveContext(m_internal->m_viewer);

    // Setup the scene
    m_internal->m_viewer->SetDefaultLights();
    m_internal->m_viewer->SetLightOn();
    m_internal->m_context->SetDisplayMode(AIS_Shaded, Standard_True);

    // Setup the rendering
    m_internal->m_view->SetBackgroundColor(Quantity_NOC_WHITE);
    m_internal->m_view->MustBeResized();
    m_internal->m_view->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_ZBUFFER);

    // Create initial content (for the example)
    makeTorus();
}

const Handle(AIS_InteractiveContext)& OcctViewer::getContext() const
{
    return m_internal->m_context;
}

void OcctViewer::paintGL()
{
    if (QOpenGLContext::currentContext() != context()) // avoid QPaint errors.
    {
        makeCurrent();
    }

    static bool color = true;
    color = ! color;
    QOpenGLFunctions *f = context()->functions();
    f->glClearColor(0.0f, color?1.0f:0.5f, 0.0f, 0.1f);

    if (!m_internal->m_view.IsNull())
    {
        m_internal->m_view->Redraw();
    }
}

void OcctViewer::resizeGL( int w, int h )
{
    if( !m_internal->m_view.IsNull() )
    {
        m_internal->m_view->MustBeResized();
    }
}

void OcctViewer::makeTorus()
{
    // Create a Torus
    gp_Ax2 axis;
    axis.SetLocation(gp_Pnt(0.0, 0.0, 0.0));

    TopoDS_Shape torus = BRepPrimAPI_MakeTorus(axis, 90.0, 30.0).Shape();
    m_internal->m_torusShape = new AIS_Shape(torus);

    m_internal->m_torusShape->SetColor(Quantity_NOC_BLUE);

    m_internal->m_context->Display(m_internal->m_torusShape, Standard_True);

    // Align the view to fit the full window with the torus
    m_internal->m_view->FitAll();
    m_internal->m_view->ZFitAll();
}

void OcctViewer::rotateScene()
{
    if (m_internal->m_torusShape.IsNull())
    {
        return;
    }

    m_internal->m_rotationAngle += 0.01; // Adjust this value to change rotation speed
    if (m_internal->m_rotationAngle >= 2 * M_PI)
    {
        m_internal->m_rotationAngle -= 2 * M_PI;
    }

    gp_Trsf rotation;
    rotation.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), m_internal->m_rotationAngle);

    m_internal->m_context->SetLocation(m_internal->m_torusShape, rotation);

    // Request a redraw
    update();
}

Upvotes: 1

Views: 280

Answers (0)

Related Questions