如何在 QOpenGLWidget 或 QQuickFramebufferObject (qt6) 上可视化 OpenCascade?

问题描述 投票:0回答:1

我可以在旧版本的 Qt 中使用

QGLWidget
(以下本页),但我在尝试更新到 Qt6 时遇到了困难。

此外,我想要一个良好的集成,以转移到 QQuickFramebufferObject 和 QtQuick2。

Attempt 1 not fully working

尝试1:

此尝试使用

WNT_Window
句柄创建
QWidget
(在 Windows 上)。当我设置
Qt::WA_PaintOnScreen
时,它“似乎”起作用,但是标准输出显示了许多消息:

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

删除

Qt::WA_PaintOnScreen
,图像会闪烁,就像 OCCT 直接在屏幕上绘制一样,然后 Qt 交换缓冲区。

    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);

尝试2:

此策略创建一个从

Aspect_NeutralWindow
句柄和当前 GL 上下文初始化的
QWidget

此解决方案无法显示任何 OCCT 内容。

    // 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);

尝试3:

此策略,在本页之后,尝试使用 GL 上下文并构建 OCCT 来使用它。

    // 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);

再次强调,OCCT 无法显示任何内容。


完整示例:

# 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();
}

c++ qt opencascade
1个回答
0
投票

旋转一下

// occView.h
#ifndef OCCTVIEWER_H
#define OCCTVIEWER_H

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

class OcctViewer : public QOpenGLWidget
{
    Q_OBJECT

    struct Internal;

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

    const Handle(AIS_InteractiveContext)& getContext() const;

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

    void makeTorus();

    std::unique_ptr<Internal> m_internal;

public slots:
    void rotateScene();
};

#endif

// occView.cpp
#include "occView.h"

#include <QOpenGLContext>
#include <QTimer>

#include <OpenGl_GraphicDriver.hxx>
#include <V3d_View.hxx>
#include <Aspect_DisplayConnection.hxx>
#include <OpenGl_Context.hxx>
#include <BRepPrimAPI_MakeTorus.hxx>
#include <AIS_Shape.hxx>
#include <Aspect_NeutralWindow.hxx>

struct OcctViewer::Internal
{
    Handle(V3d_Viewer) m_viewer;
    Handle(V3d_View) m_view;
    Handle(AIS_InteractiveContext) m_context;
    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_NoSystemBackground);
    setAutoFillBackground(false);

    connect(&m_internal->timer, &QTimer::timeout, this, &OcctViewer::rotateScene);
    m_internal->timer.setInterval(16); // ~60 FPS
    m_internal->timer.start();
}

OcctViewer::~OcctViewer() = default;

void OcctViewer::initializeGL()
{
    Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection, false);

    Handle(OpenGl_Context) oglContext = new OpenGl_Context();
    if (!oglContext->Init(QOpenGLContext::currentContext()))
    {
        throw std::runtime_error("Failed to initialize OpenGl_Context");
    }

    m_internal->m_viewer = new V3d_Viewer(graphicDriver);
    m_internal->m_view = m_internal->m_viewer->CreateView();

    Handle(Aspect_NeutralWindow) window = new Aspect_NeutralWindow();
    Aspect_Drawable drawable = (Aspect_Drawable)this->winId();
    window->SetNativeHandle(drawable);
    window->SetSize(this->width(), this->height());

    m_internal->m_view->SetWindow(window);

    if (!window->IsMapped())
    {
        window->Map();
    }

    m_internal->m_context = new AIS_InteractiveContext(m_internal->m_viewer);

    m_internal->m_viewer->SetDefaultLights();
    m_internal->m_viewer->SetLightOn();
    m_internal->m_context->SetDisplayMode(AIS_Shaded, Standard_True);

    m_internal->m_view->SetBackgroundColor(Quantity_NOC_BLACK);
    m_internal->m_view->MustBeResized();
    m_internal->m_view->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_ZBUFFER);

    makeTorus();
}

void OcctViewer::paintGL()
{
    if (m_internal->m_view->Window().IsNull())
        return;

    m_internal->m_view->Redraw();
}

void OcctViewer::resizeGL(int w, int h)
{
    if (m_internal->m_view->Window().IsNull())
        return;

    m_internal->m_view->Window()->Size(w, h);
    m_internal->m_view->MustBeResized();
}

void OcctViewer::makeTorus()
{
    gp_Ax2 axis;
    axis.SetLocation(gp_Pnt(0.0, 0.0, 0.0));

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

    m_internal->m_torusShape->SetColor(Quantity_NOC_AZURE);

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

    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.05;
    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);

    update();
}

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


主要C++

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

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

    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 3);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);

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

    return a.exec();
}

主要差异及其基本原理:

  1. 删除

    Qt::WA_PaintOnScreen

    • 此属性不是必需的,并且可能会干扰 Qt 的 OpenGL 管理。
  2. 使用

    Aspect_NeutralWindow
    代替特定于平台的窗口:

    • 这提供了更好的可移植性以及与 Qt OpenGL 上下文的集成。
  3. 显式初始化

    OpenGl_Context
    :

    • 这确保 OCCT 使用与 Qt 相同的 OpenGL 上下文。
  4. 简化

    paintGL()
    方法:

    • 删除了不必要的颜色更改和上下文检查。
  5. 更新了

    resizeGL()
    方法:

    • 现在可以正确更新 OCCT 窗口大小。
  6. main.cpp
    中添加了OpenGL格式设置:

    • 这确保了 Qt 和 OCCT OpenGL 要求之间的兼容性。
  7. CMakeLists.txt 更改:

    • 为了清晰起见,更新了项目名称和可执行文件名称。
    • 更改为 C++17 标准(对于该项目来说足够了)。
    • 简化且清晰的库链接。

这些更改旨在在 OCCT 和 Qt6 之间创建更强大的集成,解决您在渲染和上下文管理方面遇到的问题。新方法应该为基于 OCCT 的 Qt6 应用程序提供稳定的基础。

© www.soinside.com 2019 - 2024. All rights reserved.