├── .gitignore
├── images
├── occt-qopenglwidget-sample-wnt.png
└── occt-qopenglwidget-sample-x11.png
├── occt-qtquick
├── occt-qtquick.qrc
├── main.cpp
├── main6.qml
├── main5.qml
├── OcctQQuickFramebufferViewer.h
├── CMakeLists.txt
└── OcctQQuickFramebufferViewer.cpp
├── CMakeLists.txt
├── occt-qt-tools
├── CMakeLists.txt
├── OcctGlTools.h
├── OcctQtTools.h
├── OcctGlTools.cpp
└── OcctQtTools.cpp
├── .github
└── workflows
│ ├── screenshot.sh
│ ├── build_linux_gcc_qt5.yml
│ └── build_linux_gcc_qt6.yml
├── occt-qopenglwidget
├── custom.pri.template
├── OcctQMainWindowSample.h
├── main.cpp
├── occt-qopenglwidget-sample.pro
├── OcctQOpenGLWidgetViewer.h
├── CMakeLists.txt
├── OcctQMainWindowSample.cpp
└── OcctQOpenGLWidgetViewer.cpp
├── .gitattributes
├── occt-qwidget
├── OcctQMainWindowSample.h
├── main.cpp
├── OcctQWidgetViewer.h
├── CMakeLists.txt
├── OcctQMainWindowSample.cpp
└── OcctQWidgetViewer.cpp
├── .clang-format
├── LICENSE.txt
├── ReadMe.md
└── adm
└── cmake
└── FindOpenCASCADE.cmake
/.gitignore:
--------------------------------------------------------------------------------
1 | custom.pri
2 | *.pro.user
3 | CMakeLists.txt.user
4 |
--------------------------------------------------------------------------------
/images/occt-qopenglwidget-sample-wnt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gkv311/occt-samples-qt/HEAD/images/occt-qopenglwidget-sample-wnt.png
--------------------------------------------------------------------------------
/images/occt-qopenglwidget-sample-x11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gkv311/occt-samples-qt/HEAD/images/occt-qopenglwidget-sample-x11.png
--------------------------------------------------------------------------------
/occt-qtquick/occt-qtquick.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main5.qml
4 | main6.qml
5 |
6 |
7 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.13)
2 |
3 | # common solution for multiple independent projects
4 | project (occt-qt)
5 | set_property (GLOBAL PROPERTY USE_FOLDERS ON)
6 | add_subdirectory (occt-qt-tools)
7 | add_subdirectory (occt-qwidget)
8 | add_subdirectory (occt-qopenglwidget)
9 | add_subdirectory (occt-qtquick)
10 |
--------------------------------------------------------------------------------
/occt-qt-tools/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.13)
2 |
3 | # dummy project - just to collect files in folder
4 | project (occt-qt-tools)
5 | add_custom_target (${PROJECT_NAME} SOURCES
6 | OcctQtTools.h
7 | OcctQtTools.cpp
8 | OcctGlTools.h
9 | OcctGlTools.cpp
10 | ../ReadMe.md
11 | )
12 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Tools")
13 |
--------------------------------------------------------------------------------
/.github/workflows/screenshot.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | anApp=$1
4 | anImg=$2
5 | aDelay=$3
6 |
7 | "$anApp" &
8 | anAppPid=$!
9 |
10 | rm -f "$anImg"
11 | scrot -d $aDelay -F "$anImg"
12 |
13 | anImgSize=$(wc -c <"$anImg")
14 | if [ $anImgSize -lt 2000 ]; then
15 | # retry one more time
16 | mv -f "$anImg" "back-first-${anImg}"
17 | scrot -d 1 -F "$anImg"
18 | fi
19 |
20 | kill $anAppPid
21 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/custom.pri.template:
--------------------------------------------------------------------------------
1 | CASROOT=c:/occt760-vc14-64/opencascade-7.6.0
2 |
3 | CASROOT_INCLUDES=$${CASROOT}/inc
4 | MY_OCCTINCDIR = $${CASROOT}/inc
5 | MY_OCCTLIBDIR = $${CASROOT}/$${MY_PLATFORM_AND_COMPILER}/lib$${MY_BUILDTYPE}
6 | MY_OCCTBINDIR = $${CASROOT}/$${MY_PLATFORM_AND_COMPILER}/bin$${MY_BUILDTYPE}
7 |
8 | # direct building dependencies
9 | INCLUDEPATH += $${MY_OCCTINCDIR}
10 | LIBS += -L$${MY_OCCTLIBDIR}
11 |
12 | # include DLLs into PATH, including third-party libraries like FreeImage
13 | win32 {
14 | LIBS += -L$${MY_OCCTBINDIR}
15 |
16 | MY_THIRDROOT = "$${CASROOT}/.."
17 | LIBS += -L"$${MY_THIRDROOT}/freeimage-3.17.0-vc14-64/bin"
18 | LIBS += -L"$${MY_THIRDROOT}/freetype-2.5.5-vc14-64/bin"
19 | LIBS += -L"$${MY_THIRDROOT}/ffmpeg-3.3.4-64/bin"
20 | LIBS += -L"$${MY_THIRDROOT}/tbb_2017.0.100/bin/intel64/vc14"
21 | }
22 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.yml eol=lf
2 | *.qml eol=lf
3 | *.htm eol=lf
4 | *.html eol=lf
5 | *.js eol=lf
6 | *.css eol=lf
7 | *.txt eol=text
8 | *.tcl eol=lf
9 | *.cpp eol=lf
10 | *.cxx eol=lf
11 | *.gxx eol=lf
12 | *.hxx eol=lf
13 | *.lxx eol=lf
14 | *.pxx eol=lf
15 | *.igs eol=lf
16 | *.iges eol=lf
17 | *.stp eol=lf
18 | *.step eol=lf
19 | *.brep eol=lf
20 | *.rle eol=lf
21 | *.vrml eol=lf
22 | *.md eol=lf
23 | .gitignore eol=lf
24 | .gitattributes eol=lf
25 | *.wasm binary
26 | *.stl binary
27 | *.7z binary
28 | *.pdf binary
29 | *.png binary
30 | *.jpg binary
31 | *.bmp binary
32 | *.gif binary
33 | *.xwd binary
34 | *.ico binary
35 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/OcctQMainWindowSample.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #ifndef _OcctQMainWindowSample_HeaderFile
4 | #define _OcctQMainWindowSample_HeaderFile
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | class OcctQOpenGLWidgetViewer;
11 |
12 | //! Main application window.
13 | class OcctQMainWindowSample : public QMainWindow
14 | {
15 | public:
16 | //! Window constructor.
17 | OcctQMainWindowSample();
18 |
19 | private:
20 | //! Define menu bar with Quit item.
21 | void createMenuBar();
22 |
23 | //! Define controls over 3D viewer.
24 | void createLayoutOverViewer();
25 |
26 | //! Advanced method splitting 3D Viewer into sub-views.
27 | void splitSubviews();
28 |
29 | private:
30 | OcctQOpenGLWidgetViewer* myViewer = nullptr;
31 | };
32 |
33 | #endif // _OcctQMainWindowSample_HeaderFile
34 |
--------------------------------------------------------------------------------
/occt-qwidget/OcctQMainWindowSample.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #ifndef _OcctQMainWindowSample_HeaderFile
4 | #define _OcctQMainWindowSample_HeaderFile
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | class OcctQWidgetViewer;
11 |
12 | //! Main window for sample application holding OCCT 3D Viewer.
13 | class OcctQMainWindowSample : public QMainWindow
14 | {
15 | public:
16 | //! Window constructor.
17 | OcctQMainWindowSample();
18 |
19 | private:
20 | //! Define menu bar with Quit item.
21 | void createMenuBar();
22 |
23 | //! Define controls over 3D viewer.
24 | void createLayoutOverViewer();
25 |
26 | //! Advanced method splitting 3D Viewer into sub-views.
27 | void splitSubviews();
28 |
29 | private:
30 | OcctQWidgetViewer* myViewer = nullptr;
31 | };
32 |
33 | #endif // _OcctQMainWindowSample_HeaderFile
34 |
--------------------------------------------------------------------------------
/occt-qwidget/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #include "OcctQMainWindowSample.h"
4 |
5 | #include "../occt-qt-tools/OcctQtTools.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 |
13 | int main(int theNbArgs, char** theArgVec)
14 | {
15 | // before creating QApplication: define platform plugin to load (e.g. xcb on Linux)
16 | // and graphic driver (e.g. desktop OpenGL with desired profile/surface)
17 | OcctQtTools::qtGlPlatformSetup();
18 | QApplication aQApp(theNbArgs, theArgVec);
19 |
20 | QCoreApplication::setApplicationName("OCCT Qt/QWidget Viewer sample");
21 | QCoreApplication::setOrganizationName("OpenCASCADE");
22 | QCoreApplication::setApplicationVersion(OCC_VERSION_STRING_EXT);
23 |
24 | OcctQMainWindowSample aMainWindow;
25 | aMainWindow.resize(aMainWindow.sizeHint());
26 | aMainWindow.show();
27 | return aQApp.exec();
28 | }
29 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #include "OcctQMainWindowSample.h"
4 |
5 | #include "../occt-qt-tools/OcctQtTools.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 |
13 | int main(int theNbArgs, char** theArgVec)
14 | {
15 | // before creating QApplication: define platform plugin to load (e.g. xcb on Linux)
16 | // and graphic driver (e.g. desktop OpenGL with desired profile/surface)
17 | OcctQtTools::qtGlPlatformSetup();
18 | QApplication aQApp(theNbArgs, theArgVec);
19 |
20 | QCoreApplication::setApplicationName("OCCT Qt/QOpenGLWidget Viewer sample");
21 | QCoreApplication::setOrganizationName("OpenCASCADE");
22 | QCoreApplication::setApplicationVersion(OCC_VERSION_STRING_EXT);
23 |
24 | OcctQMainWindowSample aMainWindow;
25 | aMainWindow.resize(aMainWindow.sizeHint());
26 | aMainWindow.show();
27 | return aQApp.exec();
28 | }
29 |
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | # clang-format configuration file trying to apply OCCT coding style
2 | BasedOnStyle: Microsoft
3 | #
4 | # Style options
5 | AllowAllParametersOfDeclarationOnNextLine: false
6 | AllowAllArgumentsOnNextLine: false
7 | AlignAfterOpenBracket: Align
8 | AlignConsecutiveAssignments: Consecutive
9 | AlignConsecutiveDeclarations: Consecutive
10 | AlignTrailingComments: true
11 | AllowShortFunctionsOnASingleLine: Inline
12 | AlwaysBreakTemplateDeclarations: Yes
13 | BinPackArguments: false
14 | BinPackParameters: false
15 | BreakBeforeBinaryOperators: NonAssignment
16 | BreakBeforeTernaryOperators: true
17 | ColumnLimit: 120
18 | ContinuationIndentWidth: 2
19 | #ConstructorInitializerIndentWidth: 0
20 | IndentCaseLabels: true
21 | IndentPPDirectives: BeforeHash
22 | IndentWidth: 2
23 | IndentWrappedFunctionNames: true
24 | PackConstructorInitializers: Never
25 | PointerAlignment: Left
26 | ReferenceAlignment: Left
27 | SeparateDefinitionBlocks: Always
28 | SortIncludes: false
29 | UseTab: Never
30 | #
31 | # OCCT specific settings
32 | StatementMacros:
33 | - Standard_FALLTHROUGH
34 | - Standard_DEPRECATED
35 | TypenameMacros:
36 | - Handle
37 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023 Kirill Gavrilov
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/occt-qopenglwidget-sample.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 | QT += widgets
3 | greaterThan(QT_MAJOR_VERSION, 5) { QT += openglwidgets }
4 | CONFIG += console
5 |
6 | # source code of the sample
7 | HEADERS = OcctQMainWindowSample.h \
8 | OcctQOpenGLWidgetViewer.h \
9 | ../occt-qt-tools/OcctQtTools.h
10 | ../occt-qt-tools/OcctGlTools.h
11 | SOURCES = main.cpp \
12 | OcctQMainWindowSample.cpp \
13 | OcctQOpenGLWidgetViewer.cpp \
14 | ../occt-qt-tools/OcctQtTools.cpp \
15 | ../occt-qt-tools/OcctGlTools.cpp
16 | OTHER_FILES = ../LICENSE.md\
17 | ../ReadMe.md \
18 | custom.pri.template
19 |
20 | # target configuration
21 | MY_TARGET_ABI = $${QMAKE_TARGET.arch}
22 | mac { MY_TARGET_ABI = $${QMAKE_APPLE_DEVICE_ARCHS} }
23 |
24 | MY_PLATFORM = platform
25 | win32 { MY_PLATFORM = win64
26 | } else:mac { MY_PLATFORM = mac
27 | } else:linux { MY_PLATFORM = lin
28 | } else:unix { MY_PLATFORM = unix
29 | } else { warning (Unknown platform. "$$MY_PLATFORM" is used) }
30 |
31 | MY_COMPILER = compiler
32 | clang { MY_COMPILER = clang
33 | } else:gcc { MY_COMPILER = gcc
34 | } else:win32-msvc2015 { MY_COMPILER = vc14
35 | } else:win32-msvc2017 { MY_COMPILER = vc14
36 | } else:win32-msvc { MY_COMPILER = vc14
37 | } else { warning (Unknown compiler. "$$MY_COMPILER" is used) }
38 | MY_BUILDTYPE =
39 | CONFIG(debug, debug|release) { MY_BUILDTYPE = d }
40 | MY_PLATFORM_AND_COMPILER = $$MY_PLATFORM/$$MY_COMPILER
41 |
42 | # opengl32 and user32 libs required respectively for wglGetCurrentDC() and WindowFromDC()
43 | win32:LIBS += -lopengl32 -luser32
44 |
45 | # paths to OCCT should be set by custom.pri, see custom.pri.template as example
46 | exists($$PWD/custom.pri) { include($$PWD/custom.pri) }
47 |
48 | # OCCT libraries to link
49 | LIBS += -lTKernel -lTKGeomBase -lTKGeomAlgo -lTKG2d -lTKV3d -lTKG3d -lTKHLR -lTKService -lTKMath -lTKBRep -lTKTopAlgo -lTKOpenGl -lTKPrim -lTKShHealing -lTKMesh
50 |
--------------------------------------------------------------------------------
/.github/workflows/build_linux_gcc_qt5.yml:
--------------------------------------------------------------------------------
1 | name: Build (Linux/GCC/Qt5)
2 | on:
3 | push:
4 | branches: [ master ]
5 | pull_request:
6 | branches: [ master ]
7 | workflow_dispatch:
8 |
9 | jobs:
10 | Ubuntu:
11 | runs-on: ubuntu-24.04
12 | steps:
13 | - name: Clone Tree
14 | uses: actions/checkout@v1
15 | with:
16 | fetch-depth: 1
17 | - name: Install Dependencies
18 | run: |
19 | sudo apt-get update
20 | sudo apt-get install -y \
21 | g++ cmake ninja-build \
22 | libocct-draw-dev libocct-visualization-dev libocct-ocaf-dev libocct-modeling-data-dev \
23 | libocct-modeling-algorithms-dev libocct-foundation-dev libocct-data-exchange-dev \
24 | libgl-dev libegl-dev \
25 | tcllib tklib tcl-dev tk-dev \
26 | libtbb-dev \
27 | qtbase5-dev qtdeclarative5-dev qtquickcontrols2-5-dev \
28 | qml-module-qtquick-controls2 qml-module-qtquick-templates2 qml-module-qtquick-dialogs \
29 | xvfb scrot
30 | - name: Configure project
31 | run: |
32 | mkdir "build"
33 | cmake -G Ninja -S . -B "./build" -D QT_VERSION=Qt5
34 | - name: Build project
35 | run: |
36 | cmake --build "./build" --config Release
37 | - name: Run tests
38 | run: |
39 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qwidget/occt-qwidget-sample ./build/occt-qwidget.png 5
40 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qopenglwidget/occt-qopenglwidget-sample ./build/occt-qopenglwidget.png 5
41 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qtquick/occt-qtquick-sample ./build/occt-qtquick.png 5
42 | - name: Upload artifacts
43 | uses: actions/upload-artifact@v4
44 | with:
45 | name: occt-qt5
46 | path: ./build/*.png
47 |
--------------------------------------------------------------------------------
/.github/workflows/build_linux_gcc_qt6.yml:
--------------------------------------------------------------------------------
1 | name: Build (Linux/GCC/Qt6)
2 | on:
3 | push:
4 | branches: [ master ]
5 | pull_request:
6 | branches: [ master ]
7 | workflow_dispatch:
8 |
9 | jobs:
10 | Ubuntu:
11 | runs-on: ubuntu-24.04
12 | steps:
13 | - name: Clone Tree
14 | uses: actions/checkout@v1
15 | with:
16 | fetch-depth: 1
17 | - name: Install Dependencies
18 | run: |
19 | sudo apt-get update
20 | sudo apt-get install -y \
21 | g++ cmake ninja-build \
22 | libocct-draw-dev libocct-visualization-dev libocct-ocaf-dev libocct-modeling-data-dev \
23 | libocct-modeling-algorithms-dev libocct-foundation-dev libocct-data-exchange-dev \
24 | libgl-dev libegl-dev \
25 | tcllib tklib tcl-dev tk-dev \
26 | libtbb-dev \
27 | qt6-base-dev qt6-declarative-dev \
28 | qml6-module-qtquick-window qml6-module-qtquick-controls qml6-module-qtquick-templates qml6-module-qtquick-dialogs qml6-module-qtqml-workerscript \
29 | xvfb scrot
30 | - name: Configure project
31 | run: |
32 | mkdir "build"
33 | cmake -G Ninja -S . -B "./build" -D QT_VERSION=Qt6
34 | - name: Build project
35 | run: |
36 | cmake --build "./build" --config Release
37 | - name: Run tests
38 | run: |
39 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qwidget/occt-qwidget-sample ./build/occt-qwidget.png 5
40 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qopenglwidget/occt-qopenglwidget-sample ./build/occt-qopenglwidget.png 5
41 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qtquick/occt-qtquick-sample ./build/occt-qtquick.png 5
42 | - name: Upload artifacts
43 | uses: actions/upload-artifact@v4
44 | with:
45 | name: occt-qt6
46 | path: ./build/*.png
47 |
--------------------------------------------------------------------------------
/occt-qt-tools/OcctGlTools.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023 Kirill Gavrilov
2 |
3 | #ifndef _OcctGlTools_HeaderFile
4 | #define _OcctGlTools_HeaderFile
5 |
6 | #include
7 | #include
8 |
9 | class OpenGl_Context;
10 |
11 | //! Auxiliary wrapper to avoid OpenGL macros collisions between Qt and OCCT headers.
12 | class OcctGlTools
13 | {
14 | public:
15 | //! Class making DevicePixelRatio() configurable.
16 | class OcctNeutralWindow : public Aspect_NeutralWindow
17 | {
18 | public:
19 | //! Empty constructor.
20 | OcctNeutralWindow() {}
21 |
22 | //! Return device pixel ratio (logical to backing store scale factor).
23 | virtual double DevicePixelRatio() const override { return myPixelRatio; }
24 |
25 | //! Set device pixel ratio.
26 | void SetDevicePixelRatio(double theRatio) { myPixelRatio = theRatio; }
27 | private:
28 | double myPixelRatio = 1.0;
29 | };
30 | public:
31 | //! Return GL context.
32 | static Handle(OpenGl_Context) GetGlContext(const Handle(V3d_View)& theView);
33 |
34 | //! Return active native window bound to OpenGL context.
35 | static Aspect_Drawable GetGlNativeWindow(Aspect_Drawable theNativeWin);
36 |
37 | //! Initialize native window for OCCT 3D Viewer.
38 | static bool InitializeGlWindow(const Handle(V3d_View)& theView,
39 | const Aspect_Drawable theNativeWin,
40 | const Graphic3d_Vec2i& theSize,
41 | const double thePixelRatio);
42 |
43 | //! Wrap FBO created by QOpenGLFramebufferObject to OCCT 3D Viewer target.
44 | static bool InitializeGlFbo(const Handle(V3d_View)& theView);
45 |
46 | //! Cleanup up global GL state after Qt before redrawing OCCT Viewer.
47 | static void ResetGlStateBeforeOcct(const Handle(V3d_View)& theView);
48 |
49 | //! Cleanup up global GL state after OCCT before redrawing Qt.
50 | //! Alternative to QQuickOpenGLUtils::resetOpenGLState().
51 | static void ResetGlStateAfterOcct(const Handle(V3d_View)& theView);
52 | };
53 |
54 | #endif // _OcctGlTools_HeaderFile
55 |
--------------------------------------------------------------------------------
/occt-qtquick/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #include "OcctQQuickFramebufferViewer.h"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 | #include
14 |
15 | int main(int theNbArgs, char** theArgVec)
16 | {
17 | //Message::DefaultMessenger()->Printers().First()->SetTraceLevel(Message_Trace);
18 |
19 | // before creating QApplication: define platform plugin to load (e.g. xcb on Linux)
20 | // and graphic driver (e.g. desktop OpenGL with desired profile/surface)
21 | OcctQtTools::qtGlPlatformSetup();
22 |
23 | // Qt by default will attempt offloading rendering
24 | // into a separate working thread (QSGRenderThread) on some systems,
25 | // which requires addition of multi-threading synchronization mechanism
26 | // when dealing with OCCT 3D Viewer from GUI thread.
27 | // Uncomment following lines if these complexities are undesired
28 | // to ask Qt managing rendering from GUI thread.
29 | /*OSD_Environment aQsgLoop("QSG_RENDER_LOOP");
30 | if (aQsgLoop.Value().IsEmpty())
31 | {
32 | aQsgLoop.SetValue("basic");
33 | aQsgLoop.Build();
34 | }*/
35 |
36 | QApplication aQApp(theNbArgs, theArgVec);
37 |
38 | QCoreApplication::setApplicationName("OCCT Qt/QtQuick Viewer sample");
39 | QCoreApplication::setOrganizationName("OpenCASCADE");
40 | QCoreApplication::setApplicationVersion(OCC_VERSION_STRING_EXT);
41 |
42 | qmlRegisterType("OcctQQuickFramebufferViewer", 1, 0, "OcctQQuickFramebufferViewer");
43 |
44 | QQmlApplicationEngine aQmlEngine;
45 | aQmlEngine.rootContext()->setContextProperty("QT_VERSION_STR", QString(QT_VERSION_STR));
46 | aQmlEngine.rootContext()->setContextProperty("OCC_VERSION_STRING_EXT", QString(OCC_VERSION_STRING_EXT));
47 | #if (QT_VERSION_MAJOR >= 6)
48 | aQmlEngine.load(QUrl(QStringLiteral("qrc:/main6.qml")));
49 | #else
50 | aQmlEngine.load(QUrl(QStringLiteral("qrc:/main5.qml")));
51 | #endif
52 | return aQApp.exec();
53 | }
54 |
--------------------------------------------------------------------------------
/occt-qtquick/main6.qml:
--------------------------------------------------------------------------------
1 | // sample for Qt6
2 | import QtQuick
3 | import QtQuick.Controls
4 | import QtQuick.Dialogs
5 |
6 | import OcctQQuickFramebufferViewer
7 |
8 | // Sample application window
9 | ApplicationWindow {
10 | id: wnd_root
11 | title: Qt.application.name
12 | visible: true
13 |
14 | minimumWidth: 200
15 | minimumHeight: 200
16 | width: 720
17 | height: 480
18 |
19 | // OCCT 3D Viewer item
20 | OcctQQuickFramebufferViewer {
21 | id: occt_view
22 | anchors.fill: parent
23 | focus: true // to accept keyboard events
24 | }
25 |
26 | // Main menu bar
27 | MenuBar {
28 | Menu {
29 | title: qsTr("&File")
30 | MenuItem {
31 | text: qsTr("&Quit")
32 | onTriggered: Qt.quit();
33 | }
34 | }
35 | }
36 |
37 | // Viewer background color slider
38 | Slider {
39 | anchors.bottom: parent.bottom
40 | anchors.bottomMargin: 10
41 | anchors.left: parent.left
42 | anchors.leftMargin: 20
43 | anchors.right: btn_about.left
44 | anchors.rightMargin: 20
45 | from: 0
46 | value: 0
47 | to: 255
48 | onMoved: occt_view.backgroundColor = Qt.rgba(value/255.0, value/255.0, value/255.0);
49 | }
50 |
51 | // About button
52 | Rectangle {
53 | id: btn_about
54 | anchors.bottom: parent.bottom
55 | anchors.bottomMargin: 5
56 | anchors.right: parent.right
57 | anchors.rightMargin: 5
58 | radius: 10
59 | width: 100
60 | height: 50
61 | property var mainColor: "gray"
62 | color: mainColor
63 | opacity: 0.5
64 | border.color: "black"
65 | Text {
66 | text: qsTr("About")
67 | anchors.centerIn: parent
68 | }
69 |
70 | MouseArea {
71 | anchors.fill: parent
72 | onClicked: dlg_about.open()
73 | hoverEnabled: true
74 | onEntered: btn_about.color = "cyan"
75 | onExited: btn_about.color = btn_about.mainColor
76 | }
77 | }
78 |
79 | // About sample dialog
80 | MessageDialog {
81 | id: dlg_about
82 | title: qsTr("About Sample")
83 | text: qsTr("OCCT 3D Viewer sample embedded into QtQuick/QML.")
84 | informativeText: "Open CASCADE Technology v." + OCC_VERSION_STRING_EXT + "\n"
85 | + "Qt v." + QT_VERSION_STR + "\n"
86 | + "\nOpenGL info:\n" + occt_view.glInfo;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/occt-qtquick/main5.qml:
--------------------------------------------------------------------------------
1 | // sample for Qt5
2 | import QtQuick 2.7
3 | import QtQuick.Controls 2.2
4 | import QtQuick.Dialogs 1.2
5 |
6 | import OcctQQuickFramebufferViewer 1.0
7 |
8 | // Sample application window
9 | ApplicationWindow {
10 | id: wnd_root
11 | title: Qt.application.name
12 | visible: true
13 |
14 | minimumWidth: 200
15 | minimumHeight: 200
16 | width: 720
17 | height: 480
18 |
19 | // OCCT 3D Viewer item
20 | OcctQQuickFramebufferViewer {
21 | id: occt_view
22 | anchors.fill: parent
23 | focus: true // to accept keyboard events
24 | }
25 |
26 | // Main menu bar (added to Qt 5.10, QtQuick.Controls 2.3)
27 | /*MenuBar {
28 | Menu {
29 | title: qsTr("&File")
30 | MenuItem {
31 | text: qsTr("&Quit")
32 | onTriggered: Qt.quit();
33 | }
34 | }
35 | }*/
36 |
37 | // Viewer background color slider
38 | Slider {
39 | anchors.bottom: parent.bottom
40 | anchors.bottomMargin: 10
41 | anchors.left: parent.left
42 | anchors.leftMargin: 20
43 | anchors.right: btn_about.left
44 | anchors.rightMargin: 20
45 | from: 0
46 | value: 0
47 | to: 255
48 | onMoved: occt_view.backgroundColor = Qt.rgba(value/255.0, value/255.0, value/255.0);
49 | }
50 |
51 | // About button
52 | Rectangle {
53 | id: btn_about
54 | anchors.bottom: parent.bottom
55 | anchors.bottomMargin: 5
56 | anchors.right: parent.right
57 | anchors.rightMargin: 5
58 | radius: 10
59 | width: 100
60 | height: 50
61 | property var mainColor: "gray"
62 | color: mainColor
63 | opacity: 0.5
64 | border.color: "black"
65 | Text {
66 | text: qsTr("About")
67 | anchors.centerIn: parent
68 | }
69 |
70 | MouseArea {
71 | anchors.fill: parent
72 | onClicked: dlg_about.open()
73 | hoverEnabled: true
74 | onEntered: btn_about.color = "cyan"
75 | onExited: btn_about.color = btn_about.mainColor
76 | }
77 | }
78 |
79 | // About sample dialog
80 | MessageDialog {
81 | id: dlg_about
82 | title: qsTr("About Sample")
83 | text: qsTr("OCCT 3D Viewer sample embedded into QtQuick/QML.")
84 | informativeText: "Open CASCADE Technology v." + OCC_VERSION_STRING_EXT + "\n"
85 | + "Qt v." + QT_VERSION_STR + "\n"
86 | + "\nOpenGL info:\n" + occt_view.glInfo;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/OcctQOpenGLWidgetViewer.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023 Kirill Gavrilov
2 |
3 | #ifndef _OcctQOpenGLWidgetViewer_HeaderFile
4 | #define _OcctQOpenGLWidgetViewer_HeaderFile
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | class AIS_ViewCube;
16 |
17 | //! OpenGL Qt widget holding OCCT 3D View.
18 | //!
19 | //! OCCT 3D Viewer will reuse OpenGL context created by QOpenGLWidget;
20 | //! widgets on top will be blended by Qt naturally.
21 | //!
22 | //! Inheritance from AIS_ViewController is used to translate
23 | //! user input events (mouse, keyboard, window resize, etc.)
24 | //! to 3D Viewer (panning, rotation, zooming, etc.).
25 | class OcctQOpenGLWidgetViewer : public QOpenGLWidget, public AIS_ViewController
26 | {
27 | Q_OBJECT
28 | public:
29 | //! Main constructor.
30 | OcctQOpenGLWidgetViewer(QWidget* theParent = nullptr);
31 |
32 | //! Destructor.
33 | virtual ~OcctQOpenGLWidgetViewer();
34 |
35 | //! Return Viewer.
36 | const Handle(V3d_Viewer)& Viewer() const { return myViewer; }
37 |
38 | //! Return View.
39 | const Handle(V3d_View)& View() const { return myView; }
40 |
41 | //! Return AIS context.
42 | const Handle(AIS_InteractiveContext)& Context() const { return myContext; }
43 |
44 | //! Return OpenGL info.
45 | const QString& getGlInfo() const { return myGlInfo; }
46 |
47 | //! Minimal widget size.
48 | virtual QSize minimumSizeHint() const override { return QSize(200, 200); }
49 |
50 | //! Default widget size.
51 | virtual QSize sizeHint() const override { return QSize(720, 480); }
52 |
53 | public:
54 | #if (OCC_VERSION_HEX >= 0x070700)
55 | //! Handle subview focus change.
56 | virtual void OnSubviewChanged(const Handle(AIS_InteractiveContext)&,
57 | const Handle(V3d_View)&,
58 | const Handle(V3d_View)& theNewView) override;
59 | #endif
60 | protected: // OpenGL events
61 | virtual void initializeGL() override;
62 | virtual void paintGL() override;
63 | // virtual void resizeGL(int , int ) override;
64 |
65 | protected: // user input events
66 | virtual bool event(QEvent* theEvent) override;
67 | virtual void closeEvent(QCloseEvent* theEvent) override;
68 | virtual void keyPressEvent(QKeyEvent* theEvent) override;
69 | virtual void mousePressEvent(QMouseEvent* theEvent) override;
70 | virtual void mouseReleaseEvent(QMouseEvent* theEvent) override;
71 | virtual void mouseMoveEvent(QMouseEvent* theEvent) override;
72 | virtual void wheelEvent(QWheelEvent* theEvent) override;
73 |
74 | private:
75 | //! Dump OpenGL info.
76 | void dumpGlInfo(bool theIsBasic, bool theToPrint);
77 |
78 | //! Request widget paintGL() event.
79 | void updateView();
80 |
81 | //! Handle view redraw.
82 | virtual void handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView) override;
83 |
84 | private:
85 | Handle(V3d_Viewer) myViewer;
86 | Handle(V3d_View) myView;
87 | Handle(AIS_InteractiveContext) myContext;
88 | Handle(AIS_ViewCube) myViewCube;
89 |
90 | Handle(V3d_View) myFocusView;
91 |
92 | QString myGlInfo;
93 | bool myHasTouchInput = false;
94 | };
95 |
96 | #endif // _OcctQOpenGLWidgetViewer_HeaderFile
97 |
--------------------------------------------------------------------------------
/occt-qwidget/OcctQWidgetViewer.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #ifndef _OcctQWidgetViewer_HeaderFile
4 | #define _OcctQWidgetViewer_HeaderFile
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | class AIS_ViewCube;
16 |
17 | //! OpenGL Qt widget holding OCCT 3D View.
18 | //!
19 | //! OCCT 3D Viewer will create its own OpenGL context for native window of QWidget;
20 | //! widgets on top should have not semitransparent background, otherwise Qt will be unable to blend them correctly.
21 | //!
22 | //! Inheritance from AIS_ViewController is used to translate
23 | //! user input events (mouse, keyboard, window resize, etc.)
24 | //! to 3D Viewer (panning, rotation, zooming, etc.).
25 | class OcctQWidgetViewer : public QWidget, public AIS_ViewController
26 | {
27 | Q_OBJECT
28 | public:
29 | //! Main constructor.
30 | OcctQWidgetViewer(QWidget* theParent = nullptr);
31 |
32 | //! Destructor.
33 | virtual ~OcctQWidgetViewer();
34 |
35 | //! Return Viewer.
36 | const Handle(V3d_Viewer)& Viewer() const { return myViewer; }
37 |
38 | //! Return View.
39 | const Handle(V3d_View)& View() const { return myView; }
40 |
41 | //! Return AIS context.
42 | const Handle(AIS_InteractiveContext)& Context() const { return myContext; }
43 |
44 | //! Return OpenGL info.
45 | const QString& getGlInfo() const { return myGlInfo; }
46 |
47 | //! Minimal widget size.
48 | virtual QSize minimumSizeHint() const override { return QSize(200, 200); }
49 |
50 | //! Default widget size.
51 | virtual QSize sizeHint() const override { return QSize(720, 480); }
52 |
53 | public:
54 | #if (OCC_VERSION_HEX >= 0x070700)
55 | //! Handle subview focus change.
56 | virtual void OnSubviewChanged(const Handle(AIS_InteractiveContext)&,
57 | const Handle(V3d_View)&,
58 | const Handle(V3d_View)& theNewView) override;
59 | #endif
60 | protected: // drawing events
61 | //! Initial OpenGL setup.
62 | void initializeGL();
63 |
64 | //! Redraw the widget (3D Viewer).
65 | virtual void paintEvent(QPaintEvent* theEvent) override;
66 |
67 | //! Resize the widget (3D Viewer).
68 | virtual void resizeEvent(QResizeEvent* theEvent) override;
69 |
70 | //! Important - prevent Qt to try drawing in this widget.
71 | virtual QPaintEngine* paintEngine() const override { return nullptr; }
72 |
73 | protected: // user input events
74 | virtual bool event(QEvent* theEvent) override;
75 | virtual void closeEvent(QCloseEvent* theEvent) override;
76 | virtual void keyPressEvent(QKeyEvent* theEvent) override;
77 | virtual void mousePressEvent(QMouseEvent* theEvent) override;
78 | virtual void mouseReleaseEvent(QMouseEvent* theEvent) override;
79 | virtual void mouseMoveEvent(QMouseEvent* theEvent) override;
80 | virtual void wheelEvent(QWheelEvent* theEvent) override;
81 |
82 | private:
83 | //! Dump OpenGL info.
84 | void dumpGlInfo(bool theIsBasic, bool theToPrint);
85 |
86 | //! Request widget paintGL() event.
87 | void updateView();
88 |
89 | //! Handle view redraw.
90 | virtual void handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView) override;
91 |
92 | private:
93 | Handle(V3d_Viewer) myViewer;
94 | Handle(V3d_View) myView;
95 | Handle(AIS_InteractiveContext) myContext;
96 | Handle(AIS_ViewCube) myViewCube;
97 |
98 | Handle(V3d_View) myFocusView;
99 |
100 | QString myGlInfo;
101 | bool myIsCoreProfile = true;
102 | bool myHasTouchInput = false;
103 | };
104 |
105 | #endif // _OcctQWidgetViewer_HeaderFile
106 |
--------------------------------------------------------------------------------
/occt-qt-tools/OcctQtTools.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #ifndef _OcctQtTools_HeaderFile
4 | #define _OcctQtTools_HeaderFile
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | class OpenGl_Caps;
17 | class V3d_View;
18 |
19 | //! Auxiliary tools between Qt and OCCT definitions.
20 | class OcctQtTools
21 | {
22 | public: //! @name Qt application pre-setup for OCCT 3D Viewer integration
23 |
24 | //! Perform global Qt platform setup - to be called before QApplication creation.
25 | //! Defines platform plugin to load (e.g. xcb on Linux)
26 | //! and graphic driver (e.g. desktop OpenGL with desired profile/surface).
27 | static void qtGlPlatformSetup();
28 |
29 | //! Define default Qt surface format for GL context.
30 | static QSurfaceFormat qtGlSurfaceFormat(QSurfaceFormat::OpenGLContextProfile theProfile = QSurfaceFormat::NoProfile,
31 | bool theToDebug = false);
32 |
33 | //! Fill in OCCT GL caps from Qt surface format.
34 | static void qtGlCapsFromSurfaceFormat(OpenGl_Caps& theCaps, const QSurfaceFormat& theFormat);
35 |
36 | public: //! @name common conversion tools
37 |
38 | //! Map QColor into Quantity_Color.
39 | static Quantity_Color qtColorToOcct(const QColor& theColor);
40 |
41 | //! Map Quantity_Color into QColor.
42 | static QColor qtColorFromOcct(const Quantity_Color& theColor);
43 |
44 | //! Map QColor into Quantity_ColorRGBA.
45 | static Quantity_ColorRGBA qtColorToOcctRgba(const QColor& theColor);
46 |
47 | //! Map Quantity_ColorRGBA into QColor.
48 | static QColor qtColorFromOcctRgba(const Quantity_ColorRGBA& theColor);
49 |
50 | //! Map QString into TCollection_AsciiString (UTF-8).
51 | static TCollection_AsciiString qtStringToOcct(const QString& theText);
52 |
53 | //! Map TCollection_AsciiString (UTF-8) into QString.
54 | static QString qtStringFromOcct(const TCollection_AsciiString& theText);
55 |
56 | //! Map QString into TCollection_ExtendedString (UTF-16).
57 | static TCollection_ExtendedString qtStringToOcctExt(const QString& theText);
58 |
59 | //! Map TCollection_ExtendedString (UTF-16) into QString.
60 | static QString qtStringFromOcctExt(const TCollection_ExtendedString& theText);
61 |
62 | public: //! @name methods for message logs
63 |
64 | //! Map QtMsgType into Message_Gravity.
65 | static Message_Gravity qtMsgTypeToGravity(QtMsgType theType);
66 |
67 | //! Callback for qInstallMessageHandler() redirecting Qt messages to OCCT messenger.
68 | static void qtMessageHandlerToOcct(QtMsgType theType,
69 | const QMessageLogContext& theCtx,
70 | const QString& theMsg);
71 |
72 | public: //! @name methods for wrapping Qt input events into Aspect_WindowInputListener events
73 |
74 | //! Queue Qt mouse hover event to OCCT listener.
75 | static bool qtHandleHoverEvent(Aspect_WindowInputListener& theListener,
76 | const Handle(V3d_View)& theView,
77 | const QHoverEvent* theEvent);
78 |
79 | //! Queue Qt mouse event to OCCT listener.
80 | static bool qtHandleMouseEvent(Aspect_WindowInputListener& theListener,
81 | const Handle(V3d_View)& theView,
82 | const QMouseEvent* theEvent);
83 |
84 | //! Queue Qt mouse wheel event to OCCT listener.
85 | static bool qtHandleWheelEvent(Aspect_WindowInputListener& theListener,
86 | const Handle(V3d_View)& theView,
87 | const QWheelEvent* theEvent);
88 |
89 | //! Queue Qt touch event to OCCT listener.
90 | static bool qtHandleTouchEvent(Aspect_WindowInputListener& theListener,
91 | const Handle(V3d_View)& theView,
92 | const QTouchEvent* theEvent);
93 |
94 | //! Map Qt buttons bitmask to virtual keys.
95 | static Aspect_VKeyMouse qtMouseButtons2VKeys(Qt::MouseButtons theButtons);
96 |
97 | //! Map Qt mouse modifiers bitmask to virtual keys.
98 | static Aspect_VKeyFlags qtMouseModifiers2VKeys(Qt::KeyboardModifiers theModifiers);
99 |
100 | //! Map Qt key to virtual key.
101 | static Aspect_VKey qtKey2VKey(int theKey);
102 |
103 | };
104 |
105 | #endif // _OcctQtTools_HeaderFile
106 |
--------------------------------------------------------------------------------
/occt-qtquick/OcctQQuickFramebufferViewer.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #ifndef _OcctQQuickFramebufferViewer_HeaderFile
4 | #define _OcctQQuickFramebufferViewer_HeaderFile
5 |
6 | #include "../occt-qt-tools/OcctQtTools.h"
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | class AIS_ViewCube;
18 |
19 | //! OpenGL QtQuick framebuffer control holding OCCT 3D View.
20 | //!
21 | //! OCCT 3D Viewer will reuse OpenGL context created by QtQuick;
22 | //! controls on top will be blended by Qt naturally.
23 | //!
24 | //! Inheritance from AIS_ViewController is used to translate
25 | //! user input events (mouse, keyboard, window resize, etc.)
26 | //! to 3D Viewer (panning, rotation, zooming, etc.).
27 | class OcctQQuickFramebufferViewer : public QQuickFramebufferObject, public AIS_ViewController
28 | {
29 | Q_OBJECT
30 |
31 | // QML properties
32 | Q_PROPERTY(QColor backgroundColor READ getBackgroundColor WRITE setBackgroundColor)
33 | Q_PROPERTY(QString glInfo READ getGlInfo NOTIFY glInfoChanged)
34 | public:
35 | //! Main constructor.
36 | OcctQQuickFramebufferViewer(QQuickItem* theParent = nullptr);
37 |
38 | //! Destructor.
39 | virtual ~OcctQQuickFramebufferViewer();
40 |
41 | //! Return Viewer.
42 | const Handle(V3d_Viewer)& Viewer() const { return myViewer; }
43 |
44 | //! Return View.
45 | const Handle(V3d_View)& View() const { return myView; }
46 |
47 | //! Return AIS context.
48 | const Handle(AIS_InteractiveContext)& Context() const { return myContext; }
49 |
50 | public: // QML accessors
51 | //! Return OpenGL info.
52 | const QString& getGlInfo() const { return myGlInfo; }
53 |
54 | //! Return background color.
55 | QColor getBackgroundColor() { return myGlBackColor.first; }
56 |
57 | //! Set background color.
58 | void setBackgroundColor(const QColor& theColor)
59 | {
60 | myGlBackColor = std::make_pair(true, theColor);
61 | update();
62 | }
63 |
64 | signals:
65 | void glInfoChanged();
66 | void glCriticalError(QString theMsg);
67 |
68 | protected:
69 | //! OpenGL renderer interface.
70 | class Renderer : public QQuickFramebufferObject::Renderer
71 | {
72 | public:
73 | Renderer(OcctQQuickFramebufferViewer* theViewer);
74 | virtual ~Renderer();
75 | private:
76 | virtual QOpenGLFramebufferObject* createFramebufferObject(const QSize& theSize) override;
77 | virtual void synchronize(QQuickFramebufferObject* theItem) override;
78 | virtual void render() override;
79 |
80 | private:
81 | OcctQQuickFramebufferViewer* myViewer = nullptr;
82 | };
83 |
84 | virtual QQuickFramebufferObject::Renderer* createRenderer() const override;
85 | //virtual void releaseResources() override;
86 |
87 | void initializeGL(QOpenGLFramebufferObject* theFbo);
88 | void synchronize(QOpenGLFramebufferObject* theFbo);
89 | void render(QOpenGLFramebufferObject* theFbo);
90 |
91 | protected: // user input events
92 | virtual bool event(QEvent* theEvent) override;
93 | virtual void keyPressEvent(QKeyEvent* theEvent) override;
94 | virtual void mousePressEvent(QMouseEvent* theEvent) override;
95 | virtual void mouseReleaseEvent(QMouseEvent* theEvent) override;
96 | virtual void mouseMoveEvent(QMouseEvent* theEvent) override;
97 | virtual void wheelEvent(QWheelEvent* theEvent) override;
98 | virtual void hoverEnterEvent(QHoverEvent* theEvent) override;
99 | virtual void hoverMoveEvent(QHoverEvent* theEvent) override;
100 | virtual void hoverLeaveEvent(QHoverEvent* theEvent) override;
101 | //virtual void touchEvent(QTouchEvent* theEvent) override;
102 |
103 | private:
104 | //! Dump OpenGL info.
105 | void dumpGlInfo(bool theIsBasic, bool theToPrint);
106 |
107 | //! Request 3D viewer redrawing from GUI thread.
108 | void updateView();
109 |
110 | //! Handle view redraw.
111 | virtual void handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView) override;
112 |
113 | private:
114 | Handle(V3d_Viewer) myViewer;
115 | Handle(V3d_View) myView;
116 | Handle(AIS_InteractiveContext) myContext;
117 | Handle(AIS_ViewCube) myViewCube;
118 |
119 | Standard_Mutex myViewerMutex;
120 |
121 | std::pair myGlBackColor = std::make_pair(false, QColor(0, 0, 0));
122 |
123 | QString myGlInfo;
124 | bool myHasTouchInput = false;
125 | };
126 |
127 | #endif // _OcctQQuickFramebufferViewer_HeaderFile
128 |
--------------------------------------------------------------------------------
/occt-qwidget/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.13)
2 |
3 | project (occt-qwidget-sample)
4 |
5 | set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../adm/cmake" ${CMAKE_MODULE_PATH})
6 | set (APP_VERSION_MAJOR 1)
7 | set (APP_VERSION_MINOR 0)
8 |
9 | # compiler flags
10 | set (CMAKE_CXX_STANDARD 11)
11 | if (MSVC)
12 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise /EHa /MP")
13 | string (REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
14 | add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DUNICODE)
15 | else()
16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fPIC")
17 | add_definitions (-DOCC_CONVERT_SIGNALS)
18 | endif()
19 |
20 | # increase compiler warnings level (-W3 for MSVC, -Wextra for GCC)
21 | if (MSVC)
22 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
23 | string (REGEX REPLACE "/W[0-4]" "/W3" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
24 | else()
25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
26 | endif()
27 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
29 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
30 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshorten-64-to-32")
31 | endif()
32 | if (BUILD_SHARED_LIBS)
33 | if (APPLE)
34 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}")
35 | elseif (NOT WIN32)
36 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}")
37 | endif()
38 | endif()
39 | endif()
40 |
41 | # Find dependencies
42 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.")
43 | if (MSVC)
44 | set (3RDPARTY_DLL_DIRS "" CACHE STRING "Paths to external DLLs separated by semicolon (FreeImage, FreeType, etc.)")
45 | endif()
46 |
47 | # Find OpenGL
48 | if (NOT WIN32 AND NOT APPLE)
49 | set (OpenGL_GL_PREFERENCE "GLVND")
50 | endif()
51 | find_package (OpenGL REQUIRED)
52 |
53 | # Find Qt
54 | set (QT_VERSION "Qt5" CACHE STRING "Qt major version to use")
55 | set_property (CACHE QT_VERSION PROPERTY STRINGS Qt5 Qt6)
56 | set (CMAKE_AUTOUIC ON)
57 | set (CMAKE_AUTOMOC ON)
58 | set (CMAKE_AUTORCC ON)
59 | set_property (GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP "Generated Files/Moc")
60 | set_property (GLOBAL PROPERTY AUTORCC_SOURCE_GROUP "Generated Files/Resources")
61 | find_package (${QT_VERSION} COMPONENTS Widgets REQUIRED)
62 | message (STATUS "Using ${QT_VERSION} from \"${${QT_VERSION}_DIR}\"")
63 |
64 | # Find Open CASCADE Technology
65 | find_package (OpenCASCADE REQUIRED)
66 | if (NOT OpenCASCADE_FOUND)
67 | message (FATAL_ERROR "could not find OpenCASCADE, please set OpenCASCADE_DIR variable" )
68 | else()
69 | message (STATUS "Using OpenCASCADE from \"${OpenCASCADE_INSTALL_PREFIX}\"" )
70 | message (STATUS "OpenCASCADE_INCLUDE_DIR=${OpenCASCADE_INCLUDE_DIR}")
71 | message (STATUS "OpenCASCADE_LIBRARY_DIR=${OpenCASCADE_LIBRARY_DIR}")
72 | include_directories(${OpenCASCADE_INCLUDE_DIR})
73 | link_directories (${OpenCASCADE_LIBRARY_DIR})
74 | endif()
75 | set (OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel)
76 |
77 | # main project target
78 | add_executable (${PROJECT_NAME}
79 | ../occt-qt-tools/OcctQtTools.h
80 | ../occt-qt-tools/OcctQtTools.cpp
81 | ../occt-qt-tools/OcctGlTools.h
82 | main.cpp
83 | OcctQMainWindowSample.h
84 | OcctQMainWindowSample.cpp
85 | OcctQWidgetViewer.h
86 | OcctQWidgetViewer.cpp
87 | )
88 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Qt Widgets")
89 | target_link_libraries (${PROJECT_NAME} PRIVATE ${QT_VERSION}::Widgets ${OpenCASCADE_LIBS} ${OPENGL_LIBRARIES})
90 |
91 | # auxiliary development environment
92 | if (MSVC)
93 | set (X_COMPILER_BITNESS x64)
94 | get_target_property (QtCore_location ${QT_VERSION}::Core LOCATION)
95 | get_filename_component (QT_BINARY_DIR ${QtCore_location} DIRECTORY)
96 | set (QT_PLUGINS_DIR)
97 | if (EXISTS "${QT_BINARY_DIR}/../plugins")
98 | set (QT_PLUGINS_DIR "${QT_BINARY_DIR}/../plugins")
99 | endif()
100 |
101 | get_target_property (aTKernelRel "TKernel" IMPORTED_LOCATION_RELEASE)
102 | get_target_property (aTKernelDbg "TKernel" IMPORTED_LOCATION_DEBUG)
103 | get_filename_component (OpenCASCADE_BINARY_DIR_RELEASE ${aTKernelRel} DIRECTORY)
104 | get_filename_component (OpenCASCADE_BINARY_DIR_DEBUG ${aTKernelDbg} DIRECTORY)
105 | if (NOT EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}" AND EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}")
106 | set (OpenCASCADE_BINARY_DIR_DEBUG "${OpenCASCADE_BINARY_DIR_RELEASE}")
107 | elseif (NOT EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}" AND EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}")
108 | set (OpenCASCADE_BINARY_DIR_RELEASE "${OpenCASCADE_BINARY_DIR_DEBUG}")
109 | endif()
110 |
111 | set_target_properties (${PROJECT_NAME} PROPERTIES
112 | VS_DEBUGGER_ENVIRONMENT "\
113 | PATH=%PATH%;$,${OpenCASCADE_BINARY_DIR_DEBUG},${OpenCASCADE_BINARY_DIR_RELEASE}>;${3RDPARTY_DLL_DIRS};${QT_BINARY_DIR}\n\
114 | QT_PLUGIN_PATH=${QT_PLUGINS_DIR}"
115 | )
116 | endif()
117 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.13)
2 |
3 | project (occt-qopenglwidget-sample)
4 |
5 | set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../adm/cmake" ${CMAKE_MODULE_PATH})
6 | set (APP_VERSION_MAJOR 1)
7 | set (APP_VERSION_MINOR 0)
8 |
9 | # compiler flags
10 | set (CMAKE_CXX_STANDARD 11)
11 | if (MSVC)
12 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise /EHa /MP")
13 | string (REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
14 | add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DUNICODE)
15 | else()
16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fPIC")
17 | add_definitions (-DOCC_CONVERT_SIGNALS)
18 | endif()
19 |
20 | # increase compiler warnings level (-W3 for MSVC, -Wextra for GCC)
21 | if (MSVC)
22 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
23 | string (REGEX REPLACE "/W[0-4]" "/W3" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
24 | else()
25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
26 | endif()
27 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
29 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
30 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshorten-64-to-32")
31 | endif()
32 | if (BUILD_SHARED_LIBS)
33 | if (APPLE)
34 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}")
35 | elseif (NOT WIN32)
36 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}")
37 | endif()
38 | endif()
39 | endif()
40 |
41 | # Find dependencies
42 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.")
43 | if (MSVC)
44 | set (3RDPARTY_DLL_DIRS "" CACHE STRING "Paths to external DLLs separated by semicolon (FreeImage, FreeType, etc.)")
45 | endif()
46 |
47 | # Find OpenGL
48 | if (NOT WIN32 AND NOT APPLE)
49 | set (OpenGL_GL_PREFERENCE "GLVND")
50 | endif()
51 | find_package (OpenGL REQUIRED)
52 |
53 | # Find Qt
54 | set (QT_VERSION "Qt5" CACHE STRING "Qt major version to use")
55 | set_property (CACHE QT_VERSION PROPERTY STRINGS Qt5 Qt6)
56 | set (CMAKE_AUTOUIC ON)
57 | set (CMAKE_AUTOMOC ON)
58 | set (CMAKE_AUTORCC ON)
59 | set_property (GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP "Generated Files/Moc")
60 | set_property (GLOBAL PROPERTY AUTORCC_SOURCE_GROUP "Generated Files/Resources")
61 | if ("${QT_VERSION}" STREQUAL "Qt6")
62 | set (QT_COMPONENT_OPENGLWIDGETS "OpenGLWidgets")
63 | else()
64 | set (QT_COMPONENT_OPENGLWIDGETS "Widgets")
65 | endif()
66 | find_package (${QT_VERSION} COMPONENTS ${QT_COMPONENT_OPENGLWIDGETS} REQUIRED)
67 | message (STATUS "Using ${QT_VERSION} from \"${${QT_VERSION}_DIR}\"")
68 |
69 | # Find Open CASCADE Technology
70 | find_package (OpenCASCADE REQUIRED)
71 | if (NOT OpenCASCADE_FOUND)
72 | message (FATAL_ERROR "could not find OpenCASCADE, please set OpenCASCADE_DIR variable" )
73 | else()
74 | message (STATUS "Using OpenCASCADE from \"${OpenCASCADE_INSTALL_PREFIX}\"" )
75 | message (STATUS "OpenCASCADE_INCLUDE_DIR=${OpenCASCADE_INCLUDE_DIR}")
76 | message (STATUS "OpenCASCADE_LIBRARY_DIR=${OpenCASCADE_LIBRARY_DIR}")
77 | include_directories(${OpenCASCADE_INCLUDE_DIR})
78 | link_directories (${OpenCASCADE_LIBRARY_DIR})
79 | endif()
80 | set (OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel)
81 |
82 | # main project target
83 | add_executable (${PROJECT_NAME}
84 | ../occt-qt-tools/OcctQtTools.h
85 | ../occt-qt-tools/OcctQtTools.cpp
86 | ../occt-qt-tools/OcctGlTools.h
87 | ../occt-qt-tools/OcctGlTools.cpp
88 | main.cpp
89 | OcctQMainWindowSample.h
90 | OcctQMainWindowSample.cpp
91 | OcctQOpenGLWidgetViewer.h
92 | OcctQOpenGLWidgetViewer.cpp
93 | )
94 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Qt Widgets")
95 | target_link_libraries (${PROJECT_NAME} PRIVATE ${QT_VERSION}::${QT_COMPONENT_OPENGLWIDGETS} ${OpenCASCADE_LIBS} ${OPENGL_LIBRARIES})
96 |
97 | # auxiliary development environment
98 | if (MSVC)
99 | set (X_COMPILER_BITNESS x64)
100 | get_target_property (QtCore_location ${QT_VERSION}::Core LOCATION)
101 | get_filename_component (QT_BINARY_DIR ${QtCore_location} DIRECTORY)
102 | set (QT_PLUGINS_DIR)
103 | if (EXISTS "${QT_BINARY_DIR}/../plugins")
104 | set (QT_PLUGINS_DIR "${QT_BINARY_DIR}/../plugins")
105 | endif()
106 |
107 | get_target_property (aTKernelRel "TKernel" IMPORTED_LOCATION_RELEASE)
108 | get_target_property (aTKernelDbg "TKernel" IMPORTED_LOCATION_DEBUG)
109 | get_filename_component (OpenCASCADE_BINARY_DIR_RELEASE ${aTKernelRel} DIRECTORY)
110 | get_filename_component (OpenCASCADE_BINARY_DIR_DEBUG ${aTKernelDbg} DIRECTORY)
111 | if (NOT EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}" AND EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}")
112 | set (OpenCASCADE_BINARY_DIR_DEBUG "${OpenCASCADE_BINARY_DIR_RELEASE}")
113 | elseif (NOT EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}" AND EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}")
114 | set (OpenCASCADE_BINARY_DIR_RELEASE "${OpenCASCADE_BINARY_DIR_DEBUG}")
115 | endif()
116 |
117 | set_target_properties (${PROJECT_NAME} PROPERTIES
118 | VS_DEBUGGER_ENVIRONMENT "\
119 | PATH=%PATH%;$,${OpenCASCADE_BINARY_DIR_DEBUG},${OpenCASCADE_BINARY_DIR_RELEASE}>;${3RDPARTY_DLL_DIRS};${QT_BINARY_DIR}\n\
120 | QT_PLUGIN_PATH=${QT_PLUGINS_DIR}"
121 | )
122 | endif()
123 |
--------------------------------------------------------------------------------
/occt-qtquick/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.13)
2 |
3 | project (occt-qtquick-sample)
4 |
5 | set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../adm/cmake" ${CMAKE_MODULE_PATH})
6 | set (APP_VERSION_MAJOR 1)
7 | set (APP_VERSION_MINOR 0)
8 |
9 | # compiler flags
10 | set (CMAKE_CXX_STANDARD 11)
11 | if (MSVC)
12 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise /EHa /MP")
13 | string (REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
14 | add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DUNICODE)
15 | else()
16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fPIC")
17 | add_definitions (-DOCC_CONVERT_SIGNALS)
18 | endif()
19 |
20 | # increase compiler warnings level (-W3 for MSVC, -Wextra for GCC)
21 | if (MSVC)
22 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
23 | string (REGEX REPLACE "/W[0-4]" "/W3" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
24 | else()
25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
26 | endif()
27 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
29 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
30 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshorten-64-to-32")
31 | endif()
32 | if (BUILD_SHARED_LIBS)
33 | if (APPLE)
34 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}")
35 | elseif (NOT WIN32)
36 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}")
37 | endif()
38 | endif()
39 | endif()
40 |
41 | # Find dependencies
42 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.")
43 | if (MSVC)
44 | set (3RDPARTY_DLL_DIRS "" CACHE STRING "Paths to external DLLs separated by semicolon (FreeImage, FreeType, etc.)")
45 | endif()
46 |
47 | # Find OpenGL
48 | if (NOT WIN32 AND NOT APPLE)
49 | set (OpenGL_GL_PREFERENCE "GLVND")
50 | endif()
51 | find_package (OpenGL REQUIRED)
52 |
53 | # Find Qt
54 | set (QT_VERSION "Qt5" CACHE STRING "Qt major version to use")
55 | set_property (CACHE QT_VERSION PROPERTY STRINGS Qt5 Qt6)
56 | set (CMAKE_AUTOUIC ON)
57 | set (CMAKE_AUTOMOC ON)
58 | set (CMAKE_AUTORCC ON)
59 | set_property (GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP "Generated Files/Moc")
60 | set_property (GLOBAL PROPERTY AUTORCC_SOURCE_GROUP "Generated Files/Resources")
61 | set (Qt_COMPONENTS Core Gui Qml Quick QuickControls2 Widgets)
62 | set (Qt_LIBS)
63 | foreach (aLibIter ${Qt_COMPONENTS})
64 | list (APPEND Qt_LIBS "${QT_VERSION}::${aLibIter}")
65 | endforeach()
66 | find_package (${QT_VERSION} COMPONENTS ${Qt_COMPONENTS} REQUIRED)
67 | message (STATUS "Using ${QT_VERSION} from \"${${QT_VERSION}_DIR}\"")
68 |
69 | # Find Open CASCADE Technology
70 | find_package (OpenCASCADE REQUIRED)
71 | if (NOT OpenCASCADE_FOUND)
72 | message (FATAL_ERROR "could not find OpenCASCADE, please set OpenCASCADE_DIR variable" )
73 | else()
74 | message (STATUS "Using OpenCASCADE from \"${OpenCASCADE_INSTALL_PREFIX}\"" )
75 | message (STATUS "OpenCASCADE_INCLUDE_DIR=${OpenCASCADE_INCLUDE_DIR}")
76 | message (STATUS "OpenCASCADE_LIBRARY_DIR=${OpenCASCADE_LIBRARY_DIR}")
77 | include_directories(${OpenCASCADE_INCLUDE_DIR})
78 | link_directories (${OpenCASCADE_LIBRARY_DIR})
79 | endif()
80 | set (OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel)
81 |
82 | # main project target
83 | add_executable (${PROJECT_NAME}
84 | ../occt-qt-tools/OcctQtTools.h
85 | ../occt-qt-tools/OcctQtTools.cpp
86 | ../occt-qt-tools/OcctGlTools.h
87 | ../occt-qt-tools/OcctGlTools.cpp
88 | main.cpp
89 | main5.qml
90 | main6.qml
91 | OcctQQuickFramebufferViewer.h
92 | OcctQQuickFramebufferViewer.cpp
93 | occt-qtquick.qrc
94 | )
95 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Qt Quick")
96 | target_link_libraries (${PROJECT_NAME} PRIVATE
97 | ${Qt_LIBS}
98 | ${OpenCASCADE_LIBS}
99 | ${OPENGL_LIBRARIES}
100 | )
101 |
102 | # auxiliary development environment
103 | if (MSVC)
104 | set (X_COMPILER_BITNESS x64)
105 | get_target_property (QtCore_location ${QT_VERSION}::Core LOCATION)
106 | get_filename_component (QT_BINARY_DIR ${QtCore_location} DIRECTORY)
107 | set (QT_PLUGINS_DIR)
108 | if (EXISTS "${QT_BINARY_DIR}/../plugins")
109 | set (QT_PLUGINS_DIR "${QT_BINARY_DIR}/../plugins")
110 | endif()
111 |
112 | get_target_property (aTKernelRel "TKernel" IMPORTED_LOCATION_RELEASE)
113 | get_target_property (aTKernelDbg "TKernel" IMPORTED_LOCATION_DEBUG)
114 | get_filename_component (OpenCASCADE_BINARY_DIR_RELEASE ${aTKernelRel} DIRECTORY)
115 | get_filename_component (OpenCASCADE_BINARY_DIR_DEBUG ${aTKernelDbg} DIRECTORY)
116 | if (NOT EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}" AND EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}")
117 | set (OpenCASCADE_BINARY_DIR_DEBUG "${OpenCASCADE_BINARY_DIR_RELEASE}")
118 | elseif (NOT EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}" AND EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}")
119 | set (OpenCASCADE_BINARY_DIR_RELEASE "${OpenCASCADE_BINARY_DIR_DEBUG}")
120 | endif()
121 |
122 | set_target_properties (${PROJECT_NAME} PROPERTIES
123 | VS_DEBUGGER_ENVIRONMENT "\
124 | PATH=%PATH%;$,${OpenCASCADE_BINARY_DIR_DEBUG},${OpenCASCADE_BINARY_DIR_RELEASE}>;${3RDPARTY_DLL_DIRS};${QT_BINARY_DIR}\n\
125 | QT_PLUGIN_PATH=${QT_PLUGINS_DIR}"
126 | )
127 | endif()
128 |
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | OCCT 3D Viewer setup within Qt
2 | ==============================
3 |
4 | The samples in this repository demonstrate Open CASCADE Technology (OCCT) 3D Viewer setup
5 | within Qt GUI framework on Windows and Linux platforms.
6 |
7 | 
8 |
9 | ## Building
10 |
11 | Use *CMake* for building the project.
12 | See `CMakeLists.txt` in the folder defining building rules.
13 | Use `cmake-gui` or command-line interface for configuring project dependencies (Qt, OCCT).
14 | Building has been checked within OCCT 7.7.0.
15 |
16 | Make sure to copy necessary dependencies (*OCCT*, *FreeType*, *FreeImage*, etc. - depending on *OCCT* building configuration)
17 | into sample folder or configuring environment (`PATH`, `LD_LIBRARY_PATH`).
18 |
19 | *Legacy: some samples contains qmake project definition like `occt-qopenglwidget-sample.pro`,*
20 | *which could be used as alternative to CMake.*
21 | *Create `custom.pri` from `custom.pri.template` with filled in paths to OCCT.*
22 |
23 | ## OCCT Qt tools
24 |
25 | Pseudo-project within `occt-qt-tools` subfolder provides source code for gluing between Qt and OCCT:
26 | - `OcctQtTools`
27 | - conversion between common Qt and OCCT structures like colors and strings;
28 | - Qt application setup for embedding 3D viewer;
29 | - Qt input events conversion into OCCT 3D Viewer events.
30 | - `OcctGlTools` - common tools (independent from Qt) for wrapping externally created OpenGL context to setup OCCT 3D Viewer.
31 |
32 | Each Qt sample in the list below is defined independently
33 | so that it could be easily extracted to define your own application based on selected Qt module
34 | without bloating project with unnecessary code and dependencies.
35 | However, this leads to some code duplication in samples, which `OcctQtTools` tries to reduce.
36 |
37 | ## OCCT QWidget sample
38 |
39 | Project within `occt-qwidget` subfolder shows OCCT 3D viewer setup within
40 | basic `QWidget` from native window handle (`QWidget::setAttribute(Qt::WA_NativeWindow)` attribute).
41 |
42 | Within this approach, OCCT will be responsible to initialize OpenGL / OpenGL ES context on its own (Qt will be unaware of it).
43 | Unlike `QOpenGLWidget` sample, the widgets on top of `QWidget` holding 3D Viewer should have solid background color,
44 | as Qt will be unable to blend widgets together on its own.
45 |
46 | *Notice: `QOpenGLWidget` (see next) is a preferred way for integrating OpenGL viewer into Qt Widgets application.*
47 | *This `QWidget` sample demonstrates a working approach commonly used by applications based on Qt3/Qt4 and other GUI frameworks.*
48 |
49 | ## OCCT QOpenGLWidget sample
50 |
51 | Project within `occt-qopenglwidget` subfolder shows OCCT 3D viewer setup
52 | from *OpenGL* context created by `QOpenGLWidget` within Qt Widgets application.
53 |
54 | The approach with `QOpenGLWidget` requires careful gluing layer for Qt and OCCT 3D Viewer to share common OpenGL context.
55 | Unlike `QWidget` sample, the widgets on top of `QOpenGLWidget` holding 3D Viewer might have semitransparent background color,
56 | as Qt will be able to blend widgets together on its own.
57 |
58 | 
59 |
60 | ## OCCT QtQuick/QML sample
61 |
62 | Project within `occt-qtquick` subfolder shows OCCT 3D viewer setup
63 | from *OpenGL* context created by `QQuickFramebufferObject` within Qt5 QtQuick/QML application.
64 |
65 | The approach with `QQuickFramebufferObject` requires careful gluing layer for Qt and OCCT 3D Viewer to share common OpenGL context.
66 |
67 | ## Common tips
68 |
69 | ### QSGRenderThread
70 |
71 | `QtQuick` by default will attempt offloading rendering into a separate working thread (`QSGRenderThread`) on some systems,
72 | which might improve application GUI performance in some cases.
73 |
74 | This, however, requires addition of multithreading synchronization mechanism when dealing with OCCT 3D Viewer from GUI thread.
75 | Uncomment lines setting `QSG_RENDER_LOOP` environment variable,
76 | if these complexities are undesired for your application to ask Qt managing rendering from GUI thread.
77 |
78 | It is still responsibility of application logic to offload expensive operations (e.g. not GUI and rendering)
79 | into a separate threads - see next tip.
80 |
81 | ### Background thread for expensive operations
82 |
83 | CAD applications usually perform many operations that might take considerable time.
84 | When called from the main (or rendering) threads, these operations will make GUI and 3D Viewer unresponsive.
85 | Apart from poor user experience, some system might suggest to terminate hanged applications.
86 |
87 | The preferred approach is to offload into a background thread operations like:
88 | - Blocking operations (reading from files, network);
89 | - Import/export of models;
90 | - Modeling operations (Booleans and others);
91 | - Other expensive algorithms (like `BRepMesh`).
92 |
93 | ### Message log
94 |
95 | Provide `Message_Printer` implementation to redirect messages coming from OCCT algorithms to end user (GUI)
96 | or to application logs (for developers).
97 |
98 | Define your own handler for Qt messages via `qInstallMessageHandler`;
99 | redirect these messages to `Message::DefaultMessenger()`, to GUI or to application logs.
100 |
101 | ### Exception handling
102 |
103 | OCCT algorithms may raise C++ exceptions.
104 | Don't forget to put algorithms into `try`/`catch` blocks to prevent GUI crashes.
105 |
106 | Override `QApplication::notify()` to catch `Standard_Failure` exceptions
107 | for gracefully handling internal/algorithmic errors to end user without crashing application.
108 |
109 | Use `OSD::SetSignal()` to further protect application from crashes due to internal OCCT errors,
110 | although this mechanism may hinder such OCCT bugs and should be used with caution.
111 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/OcctQMainWindowSample.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #include "OcctQMainWindowSample.h"
4 |
5 | #include "OcctQOpenGLWidgetViewer.h"
6 |
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | // ================================================================
20 | // Function : OcctQMainWindowSample
21 | // ================================================================
22 | OcctQMainWindowSample::OcctQMainWindowSample()
23 | {
24 | // 3D Viewer widget as a central widget
25 | myViewer = new OcctQOpenGLWidgetViewer();
26 | setCentralWidget(myViewer);
27 |
28 | // menu bar
29 | createMenuBar();
30 |
31 | // some controls on top of 3D Viewer
32 | createLayoutOverViewer();
33 | }
34 |
35 | // ================================================================
36 | // Function : createMenuBar
37 | // ================================================================
38 | void OcctQMainWindowSample::createMenuBar()
39 | {
40 | QMenuBar* aMenuBar = new QMenuBar();
41 | QMenu* aMenuWindow = aMenuBar->addMenu("&File");
42 | #if (OCC_VERSION_HEX >= 0x070700)
43 | {
44 | QAction* anActionSplit = new QAction(aMenuWindow);
45 | anActionSplit->setText("Split Views");
46 | aMenuWindow->addAction(anActionSplit);
47 | connect(anActionSplit, &QAction::triggered, [this]() { splitSubviews(); });
48 | }
49 | #endif
50 | {
51 | QAction* anActionQuit = new QAction(aMenuWindow);
52 | anActionQuit->setText("Quit");
53 | aMenuWindow->addAction(anActionQuit);
54 | connect(anActionQuit, &QAction::triggered, [this]() { close(); });
55 | }
56 | setMenuBar(aMenuBar);
57 | }
58 |
59 | // ================================================================
60 | // Function : createLayoutOverViewer
61 | // ================================================================
62 | void OcctQMainWindowSample::createLayoutOverViewer()
63 | {
64 | QVBoxLayout* aLayout = new QVBoxLayout(myViewer);
65 | aLayout->setDirection(QBoxLayout::BottomToTop);
66 | aLayout->setAlignment(Qt::AlignBottom);
67 | {
68 | // button displaying message window with OpenGL info
69 | QPushButton* aQuitBtn = new QPushButton("About");
70 | aLayout->addWidget(aQuitBtn);
71 | connect(aQuitBtn, &QPushButton::clicked, [this]() {
72 | QMessageBox::information(0,
73 | "About Sample",
74 | QString() + "OCCT 3D Viewer sample embedded into Qt Widgets.\n\n"
75 | + "Open CASCADE Technology v." OCC_VERSION_STRING_EXT "\n"
76 | + "Qt v." QT_VERSION_STR "\n\n" + "OpenGL info:\n" + myViewer->getGlInfo());
77 | });
78 | }
79 | {
80 | // slider changing viewer background color
81 |
82 | // the widgets on top of OCCT 3D Viewer (implemented as QOpenGLWidget) might have transparent background
83 | QWidget* aSliderBox = new QWidget();
84 | aSliderBox->setStyleSheet("QWidget { background-color: rgba(0, 0, 0, 0); }");
85 |
86 | QHBoxLayout* aSliderLayout = new QHBoxLayout(aSliderBox);
87 | {
88 | QLabel* aSliderLabel = new QLabel("Background");
89 | aSliderLabel->setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 0); color: white; }");
90 | aSliderLabel->setGeometry(50, 50, 50, 50);
91 | aSliderLabel->adjustSize();
92 | aSliderLayout->addWidget(aSliderLabel);
93 | }
94 | {
95 | QSlider* aSlider = new QSlider(Qt::Horizontal);
96 | aSlider->setRange(0, 255);
97 | aSlider->setSingleStep(1);
98 | aSlider->setPageStep(15);
99 | aSlider->setTickInterval(15);
100 | aSlider->setTickPosition(QSlider::TicksRight);
101 | aSlider->setValue(0);
102 | aSliderLayout->addWidget(aSlider);
103 | connect(aSlider, &QSlider::valueChanged, [this](int theValue) {
104 | const float aVal = theValue / 255.0f;
105 | const Quantity_Color aColor(aVal, aVal, aVal, Quantity_TOC_sRGB);
106 | #if (OCC_VERSION_HEX >= 0x070700)
107 | for (const Handle(V3d_View)& aSubviewIter : myViewer->View()->Subviews())
108 | {
109 | aSubviewIter->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical);
110 | aSubviewIter->Invalidate();
111 | }
112 | #endif
113 | // myViewer->View()->SetBackgroundColor(aColor);
114 | myViewer->View()->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical);
115 | myViewer->View()->Invalidate();
116 | myViewer->update();
117 | });
118 | }
119 |
120 | aLayout->addWidget(aSliderBox);
121 | }
122 | }
123 |
124 | // ================================================================
125 | // Function : splitSubviews
126 | // ================================================================
127 | void OcctQMainWindowSample::splitSubviews()
128 | {
129 | #if (OCC_VERSION_HEX >= 0x070700)
130 | if (!myViewer->View()->Subviews().IsEmpty())
131 | {
132 | // remove subviews
133 | myViewer->View()->View()->SetSubviewComposer(false);
134 | NCollection_Sequence aSubviews = myViewer->View()->Subviews();
135 | for (const Handle(V3d_View)& aSubviewIter : aSubviews)
136 | aSubviewIter->Remove();
137 |
138 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, myViewer->View());
139 | }
140 | else
141 | {
142 | // create two subviews splitting window horizontally
143 | myViewer->View()->View()->SetSubviewComposer(true);
144 |
145 | Handle(V3d_View) aSubView1 = new V3d_View(myViewer->Viewer());
146 | aSubView1->SetImmediateUpdate(false);
147 | aSubView1->SetWindow(myViewer->View(),
148 | Graphic3d_Vec2d(0.5, 1.0),
149 | Aspect_TOTP_LEFT_UPPER,
150 | Graphic3d_Vec2d(0.0, 0.0));
151 |
152 | Handle(V3d_View) aSubView2 = new V3d_View(myViewer->Viewer());
153 | aSubView2->SetImmediateUpdate(false);
154 | aSubView2->SetWindow(myViewer->View(),
155 | Graphic3d_Vec2d(0.5, 1.0),
156 | Aspect_TOTP_LEFT_UPPER,
157 | Graphic3d_Vec2d(0.5, 0.0));
158 |
159 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, aSubView1);
160 | }
161 | myViewer->View()->Invalidate();
162 | myViewer->update();
163 | #endif
164 | }
165 |
--------------------------------------------------------------------------------
/occt-qwidget/OcctQMainWindowSample.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #include "OcctQMainWindowSample.h"
4 |
5 | #include "OcctQWidgetViewer.h"
6 |
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | // ================================================================
20 | // Function : OcctQMainWindowSample
21 | // ================================================================
22 | OcctQMainWindowSample::OcctQMainWindowSample()
23 | {
24 | // 3D Viewer widget as a central widget
25 | myViewer = new OcctQWidgetViewer();
26 | setCentralWidget(myViewer);
27 |
28 | // menu bar
29 | createMenuBar();
30 |
31 | // some controls on top of 3D Viewer
32 | createLayoutOverViewer();
33 | }
34 |
35 | // ================================================================
36 | // Function : createMenuBar
37 | // ================================================================
38 | void OcctQMainWindowSample::createMenuBar()
39 | {
40 | QMenuBar* aMenuBar = new QMenuBar();
41 | QMenu* aMenuWindow = aMenuBar->addMenu("&File");
42 | #if (OCC_VERSION_HEX >= 0x070700)
43 | {
44 | QAction* anActionSplit = new QAction(aMenuWindow);
45 | anActionSplit->setText("Split Views");
46 | aMenuWindow->addAction(anActionSplit);
47 | connect(anActionSplit, &QAction::triggered, [this]() { splitSubviews(); });
48 | }
49 | #endif
50 | {
51 | QAction* anActionQuit = new QAction(aMenuWindow);
52 | anActionQuit->setText("Quit");
53 | aMenuWindow->addAction(anActionQuit);
54 | connect(anActionQuit, &QAction::triggered, [this]() { close(); });
55 | }
56 | setMenuBar(aMenuBar);
57 | }
58 |
59 | // ================================================================
60 | // Function : createLayoutOverViewer
61 | // ================================================================
62 | void OcctQMainWindowSample::createLayoutOverViewer()
63 | {
64 | QVBoxLayout* aLayout = new QVBoxLayout(myViewer);
65 | aLayout->setDirection(QBoxLayout::BottomToTop);
66 | aLayout->setAlignment(Qt::AlignBottom);
67 | {
68 | // button displaying message window with OpenGL info
69 | QPushButton* aQuitBtn = new QPushButton("About");
70 | aLayout->addWidget(aQuitBtn);
71 | connect(aQuitBtn, &QPushButton::clicked, [this]() {
72 | QMessageBox::information(0,
73 | "About Sample",
74 | QString() + "OCCT 3D Viewer sample embedded into Qt Widgets.\n\n"
75 | + "Open CASCADE Technology v." OCC_VERSION_STRING_EXT "\n"
76 | + "Qt v." QT_VERSION_STR "\n\n" + "OpenGL info:\n" + myViewer->getGlInfo());
77 | });
78 | }
79 | {
80 | // slider changing viewer background color
81 |
82 | // make sure widget on top of OCCT 3D Viewer (implemented as QWidget and not QOpenGLWidget)
83 | // is filled with color - semitransparent widgets will have artifacts
84 | QWidget* aSliderBox = new QWidget();
85 | aSliderBox->setStyleSheet("QWidget { background-color: black; }");
86 |
87 | QHBoxLayout* aSliderLayout = new QHBoxLayout(aSliderBox);
88 | {
89 | QLabel* aSliderLabel = new QLabel("Background");
90 | aSliderLabel->setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 0); color: white; }");
91 | aSliderLabel->setGeometry(50, 50, 50, 50);
92 | aSliderLabel->adjustSize();
93 | aSliderLayout->addWidget(aSliderLabel);
94 | }
95 | {
96 | QSlider* aSlider = new QSlider(Qt::Horizontal);
97 | aSlider->setRange(0, 255);
98 | aSlider->setSingleStep(1);
99 | aSlider->setPageStep(15);
100 | aSlider->setTickInterval(15);
101 | aSlider->setTickPosition(QSlider::TicksRight);
102 | aSlider->setValue(0);
103 | aSliderLayout->addWidget(aSlider);
104 | connect(aSlider, &QSlider::valueChanged, [this](int theValue) {
105 | const float aVal = theValue / 255.0f;
106 | const Quantity_Color aColor(aVal, aVal, aVal, Quantity_TOC_sRGB);
107 | #if (OCC_VERSION_HEX >= 0x070700)
108 | for (const Handle(V3d_View)& aSubviewIter : myViewer->View()->Subviews())
109 | {
110 | aSubviewIter->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical);
111 | aSubviewIter->Invalidate();
112 | }
113 | #endif
114 | // myViewer->View()->SetBackgroundColor(aColor);
115 | myViewer->View()->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical);
116 | myViewer->View()->Invalidate();
117 | myViewer->update();
118 | });
119 | }
120 |
121 | aLayout->addWidget(aSliderBox);
122 | }
123 | }
124 |
125 | // ================================================================
126 | // Function : splitSubviews
127 | // ================================================================
128 | void OcctQMainWindowSample::splitSubviews()
129 | {
130 | #if (OCC_VERSION_HEX >= 0x070700)
131 | if (!myViewer->View()->Subviews().IsEmpty())
132 | {
133 | // remove subviews
134 | myViewer->View()->View()->SetSubviewComposer(false);
135 | NCollection_Sequence aSubviews = myViewer->View()->Subviews();
136 | for (const Handle(V3d_View)& aSubviewIter : aSubviews)
137 | aSubviewIter->Remove();
138 |
139 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, myViewer->View());
140 | }
141 | else
142 | {
143 | // create two subviews splitting window horizontally
144 | myViewer->View()->View()->SetSubviewComposer(true);
145 |
146 | Handle(V3d_View) aSubView1 = new V3d_View(myViewer->Viewer());
147 | aSubView1->SetImmediateUpdate(false);
148 | aSubView1->SetWindow(myViewer->View(),
149 | Graphic3d_Vec2d(0.5, 1.0),
150 | Aspect_TOTP_LEFT_UPPER,
151 | Graphic3d_Vec2d(0.0, 0.0));
152 |
153 | Handle(V3d_View) aSubView2 = new V3d_View(myViewer->Viewer());
154 | aSubView2->SetImmediateUpdate(false);
155 | aSubView2->SetWindow(myViewer->View(),
156 | Graphic3d_Vec2d(0.5, 1.0),
157 | Aspect_TOTP_LEFT_UPPER,
158 | Graphic3d_Vec2d(0.5, 0.0));
159 |
160 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, aSubView1);
161 | }
162 | myViewer->View()->Invalidate();
163 | myViewer->update();
164 | #endif
165 | }
166 |
--------------------------------------------------------------------------------
/occt-qt-tools/OcctGlTools.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023 Kirill Gavrilov
2 |
3 | #ifdef _WIN32
4 | #include
5 | #endif
6 |
7 | #include "OcctGlTools.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | // Exporting this symbol from .exe with value=1 will direct to NVIDIA GPU on Optimus systems
16 | //__declspec(dllexport) DWORD NvOptimusEnablement = 1;
17 | // Exporting this symbol from .exe with value=1 will direct to faster GPU on AMD PowerXpress systems
18 | //__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
19 |
20 | //! OpenGL FBO subclass for wrapping FBO created by Qt using GL_RGBA8
21 | //! texture format instead of GL_SRGB8_ALPHA8.
22 | //! This FBO is set to OpenGl_Context::SetDefaultFrameBuffer() as a final target.
23 | //! Subclass calls OpenGl_Context::SetFrameBufferSRGB() with sRGB=false flag,
24 | //! which asks OCCT to disable GL_FRAMEBUFFER_SRGB and apply sRGB gamma correction manually.
25 | class OcctQtFrameBuffer : public OpenGl_FrameBuffer
26 | {
27 | DEFINE_STANDARD_RTTI_INLINE(OcctQtFrameBuffer, OpenGl_FrameBuffer)
28 | public:
29 | //! Empty constructor.
30 | OcctQtFrameBuffer() {}
31 |
32 | //! Make this FBO active in context.
33 | virtual void BindBuffer(const Handle(OpenGl_Context)& theGlCtx) override
34 | {
35 | OpenGl_FrameBuffer::BindBuffer(theGlCtx);
36 | theGlCtx->SetFrameBufferSRGB(true, false);
37 | }
38 |
39 | //! Make this FBO as drawing target in context.
40 | virtual void BindDrawBuffer(const Handle(OpenGl_Context)& theGlCtx) override
41 | {
42 | OpenGl_FrameBuffer::BindDrawBuffer(theGlCtx);
43 | theGlCtx->SetFrameBufferSRGB(true, false);
44 | }
45 |
46 | //! Make this FBO as reading source in context.
47 | virtual void BindReadBuffer(const Handle(OpenGl_Context)& theGlCtx) override
48 | {
49 | OpenGl_FrameBuffer::BindReadBuffer(theGlCtx);
50 | }
51 | };
52 |
53 | // ================================================================
54 | // Function : GetGlContext
55 | // ================================================================
56 | Handle(OpenGl_Context) OcctGlTools::GetGlContext(const Handle(V3d_View)& theView)
57 | {
58 | Handle(OpenGl_View) aGlView = Handle(OpenGl_View)::DownCast(theView->View());
59 | return aGlView->GlWindow()->GetGlContext();
60 | }
61 |
62 | // ================================================================
63 | // Function : GetGlNativeWindow
64 | // ================================================================
65 | Aspect_Drawable OcctGlTools::GetGlNativeWindow(Aspect_Drawable theNativeWin)
66 | {
67 | Aspect_Drawable aNativeWin = (Aspect_Drawable)theNativeWin;
68 | #ifdef _WIN32
69 | HDC aWglDevCtx = wglGetCurrentDC();
70 | HWND aWglWin = WindowFromDC(aWglDevCtx);
71 | aNativeWin = (Aspect_Drawable)aWglWin;
72 | #endif
73 | return aNativeWin;
74 | }
75 |
76 | // ================================================================
77 | // Function : InitializeGlWindow
78 | // ================================================================
79 | bool OcctGlTools::InitializeGlWindow(const Handle(V3d_View)& theView,
80 | const Aspect_Drawable theNativeWin,
81 | const Graphic3d_Vec2i& theSize,
82 | const double thePixelRatio)
83 | {
84 | const Aspect_Drawable aNativeWin = GetGlNativeWindow(theNativeWin);
85 |
86 | Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast(theView->Viewer()->Driver());
87 | Handle(OpenGl_Context) aGlCtx = new OpenGl_Context();
88 | if (!aGlCtx->Init(!aDriver->Options().contextCompatible))
89 | {
90 | Message::SendFail() << "Error: OpenGl_Context is unable to wrap OpenGL context";
91 | return false;
92 | }
93 |
94 | Handle(OcctNeutralWindow) aWindow = Handle(OcctNeutralWindow)::DownCast(theView->Window());
95 | if (aWindow.IsNull())
96 | {
97 | aWindow = new OcctNeutralWindow();
98 | aWindow->SetVirtual(true);
99 | }
100 | aWindow->SetNativeHandle(aNativeWin);
101 | aWindow->SetSize(theSize.x(), theSize.y());
102 | aWindow->SetDevicePixelRatio(thePixelRatio);
103 | theView->SetWindow(aWindow, aGlCtx->RenderingContext());
104 | theView->MustBeResized();
105 | theView->Invalidate();
106 | #if (OCC_VERSION_HEX >= 0x070700)
107 | for (const Handle(V3d_View)& aSubviewIter : theView->Subviews())
108 | {
109 | aSubviewIter->MustBeResized();
110 | aSubviewIter->Invalidate();
111 | }
112 | #endif
113 | return true;
114 | }
115 |
116 | // ================================================================
117 | // Function : InitializeGlFbo
118 | // ================================================================
119 | bool OcctGlTools::InitializeGlFbo(const Handle(V3d_View)& theView)
120 | {
121 | Handle(OpenGl_Context) aGlCtx = OcctGlTools::GetGlContext(theView);
122 | Handle(OpenGl_FrameBuffer) aDefaultFbo = aGlCtx->DefaultFrameBuffer();
123 | if (aDefaultFbo.IsNull())
124 | {
125 | aDefaultFbo = new OcctQtFrameBuffer();
126 | aGlCtx->SetDefaultFrameBuffer(aDefaultFbo);
127 | }
128 | if (!aDefaultFbo->InitWrapper(aGlCtx))
129 | {
130 | aDefaultFbo.Nullify();
131 | Message::DefaultMessenger()->Send("Default FBO wrapper creation failed", Message_Fail);
132 | return false;
133 | }
134 |
135 | Graphic3d_Vec2i aViewSizeOld;
136 | const Graphic3d_Vec2i aViewSizeNew = aDefaultFbo->GetVPSize();
137 | Handle(OcctNeutralWindow) aWindow = Handle(OcctNeutralWindow)::DownCast(theView->Window());
138 | aWindow->Size(aViewSizeOld.x(), aViewSizeOld.y());
139 | if (aViewSizeNew != aViewSizeOld)
140 | {
141 | aWindow->SetSize(aViewSizeNew.x(), aViewSizeNew.y());
142 | theView->MustBeResized();
143 | theView->Invalidate();
144 | #if (OCC_VERSION_HEX >= 0x070700)
145 | for (const Handle(V3d_View)& aSubviewIter : theView->Subviews())
146 | {
147 | aSubviewIter->MustBeResized();
148 | aSubviewIter->Invalidate();
149 | aDefaultFbo->SetupViewport(aGlCtx);
150 | }
151 | #endif
152 | }
153 | return true;
154 | }
155 |
156 | // ================================================================
157 | // Function : ResetGlStateBeforeOcct
158 | // ================================================================
159 | void OcctGlTools::ResetGlStateBeforeOcct(const Handle(V3d_View)& theView)
160 | {
161 | Handle(OpenGl_Context) aGlCtx = GetGlContext(theView);
162 | if (aGlCtx.IsNull())
163 | return;
164 |
165 | if (aGlCtx->core20fwd != nullptr)
166 | {
167 | // shouldn't be a problem in most cases, but make sure to unbind active GLSL program
168 | aGlCtx->core20fwd->glUseProgram(0);
169 | }
170 |
171 | // Qt leaves GL_BLEND enabled after drawing semitransparent elements,
172 | // but OCCT doesn't reset its state before drawing opaque objects.
173 | // Disable also texture bindings left by Qt.
174 | aGlCtx->core11fwd->glBindTexture(GL_TEXTURE_2D, 0);
175 | aGlCtx->core11fwd->glDisable(GL_BLEND);
176 | if (aGlCtx->core11ffp != nullptr)
177 | {
178 | aGlCtx->core11fwd->glDisable(GL_ALPHA_TEST);
179 | aGlCtx->core11fwd->glDisable(GL_TEXTURE_2D);
180 | }
181 | }
182 |
183 | // ================================================================
184 | // Function : ResetGlStateAfterOcct
185 | // ================================================================
186 | void OcctGlTools::ResetGlStateAfterOcct(const Handle(V3d_View)& theView)
187 | {
188 | Handle(OpenGl_Context) aGlCtx = GetGlContext(theView);
189 | if (aGlCtx.IsNull())
190 | return;
191 |
192 | // Qt expects default OpenGL pack/unpack alignment setup,
193 | // while OCCT manages it dynamically;
194 | // without resetting alignment setup, Qt will draw
195 | // some textures corrupted (like fonts)
196 | aGlCtx->core11fwd->glPixelStorei(GL_PACK_ALIGNMENT, 4);
197 | aGlCtx->core11fwd->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
198 |
199 | if (aGlCtx->core15fwd != nullptr)
200 | {
201 | // Qt expects first texture object to be bound,
202 | // but OCCT might leave another object bound within multi-texture mapping
203 | aGlCtx->core15fwd->glActiveTexture(GL_TEXTURE0);
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/adm/cmake/FindOpenCASCADE.cmake:
--------------------------------------------------------------------------------
1 | # This script finds OpenCASCADE Technology libraries.
2 | # The script requires:
3 | # OpenCASCADE_DIR - root OCCT folder or folder with CMake configuration files
4 | #
5 | # Script will define the following variables on success:
6 | # OpenCASCADE_FOUND - package is successfully found
7 | # OpenCASCADE_INCLUDE_DIR - directory with headers
8 | # OpenCASCADE_LIBRARY_DIR - directory with libraries for linker
9 | # OpenCASCADE_BINARY_DIR - directory with DLLs
10 | # OpenCASCADE_RESOURCE_DIR - directory with resource files
11 | include(FindPackageHandleStandardArgs)
12 |
13 | # MY_PLATFORM variable
14 | math (EXPR MY_BITNESS "32 + 32*(${CMAKE_SIZEOF_VOID_P}/8)")
15 | if (WIN32)
16 | set (MY_PLATFORM "win${MY_BITNESS}")
17 | elseif(APPLE)
18 | set (MY_PLATFORM "mac")
19 | else()
20 | set (MY_PLATFORM "lin")
21 | endif()
22 |
23 | # MY_PLATFORM_AND_COMPILER variable
24 | if (MSVC)
25 | if (MSVC90)
26 | set (MY_COMPILER vc9)
27 | elseif (MSVC10)
28 | set (MY_COMPILER vc10)
29 | elseif (MSVC11)
30 | set (MY_COMPILER vc11)
31 | elseif (MSVC12)
32 | set (MY_COMPILER vc12)
33 | elseif (MSVC14)
34 | set (MY_COMPILER vc14)
35 | else()
36 | set (MY_COMPILER vc15)
37 | message (WARNING "Unknown msvc version. $$MY_COMPILER is used")
38 | endif()
39 | elseif (DEFINED CMAKE_COMPILER_IS_GNUCC)
40 | set (MY_COMPILER gcc)
41 | elseif (DEFINED CMAKE_COMPILER_IS_GNUCXX)
42 | set (MY_COMPILER gcc)
43 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
44 | set (MY_COMPILER clang)
45 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
46 | set (MY_COMPILER icc)
47 | else()
48 | set (MY_COMPILER ${CMAKE_GENERATOR})
49 | string (REGEX REPLACE " " "" COMPILER ${MY_COMPILER})
50 | endif()
51 | set (MY_PLATFORM_AND_COMPILER "${MY_PLATFORM}/${MY_COMPILER}")
52 | message (STATUS "MY_PLATFORM_AND_COMPILER=${MY_PLATFORM_AND_COMPILER}")
53 |
54 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.")
55 |
56 | # default paths
57 | set (OpenCASCADE_INCLUDE_DIR "${OpenCASCADE_DIR}/inc")
58 | set (OpenCASCADE_LIBRARY_DIR "${OpenCASCADE_DIR}/${MY_PLATFORM_AND_COMPILER}/lib")
59 | set (OpenCASCADE_BINARY_DIR "${OpenCASCADE_DIR}/${MY_PLATFORM_AND_COMPILER}/bin")
60 | set (OpenCASCADE_RESOURCE_DIR "${OpenCASCADE_DIR}/src")
61 |
62 | # complete list of OCCT Toolkits (copy-paste from adm/UDLIST, since installed OCCT does not include UDLIST)
63 | set (OpenCASCADE_TKLIST "")
64 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKernel TKMath) # FoundationClasses
65 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKG2d TKG3d TKGeomBase TKBRep) # ModelingData
66 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKGeomAlgo TKTopAlgo TKPrim TKBO TKBool TKHLR TKFillet TKOffset TKFeat TKMesh TKXMesh TKShHealing) # ModelingAlgorithms
67 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKService TKV3d TKOpenGl TKMeshVS TKIVtk TKD3DHost) # Visualization
68 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKCDF TKLCAF TKCAF TKBinL TKXmlL TKBin TKXml TKStdL TKStd TKTObj TKBinTObj TKXmlTObj TKVCAF) # ApplicationFramework
69 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES TKXCAF TKXDEIGES TKXDESTEP TKSTL TKVRML TKXmlXCAF TKBinXCAF TKRWMesh) # DataExchange
70 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKDraw TKViewerTest TKTopTest TKOpenGlTest) # Draw
71 |
72 | # validate location of OCCT libraries and headers
73 | set (OpenCASCADE_INCLUDE_DIR_FOUND)
74 | set (OpenCASCADE_LIBRARY_DIR_FOUND)
75 | set (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND)
76 | set (OpenCASCADE_IMPLIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX})
77 | set (OpenCASCADE_SHAREDLIB_RELEASE_FOUND)
78 | set (OpenCASCADE_SHAREDLIB_DEBUG_FOUND)
79 | if (EXISTS "${OpenCASCADE_INCLUDE_DIR}/Standard.hxx")
80 | set (OpenCASCADE_INCLUDE_DIR_FOUND ON)
81 | endif()
82 |
83 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_STATIC_LIBRARY_SUFFIX}")
84 | set (OpenCASCADE_LIBRARY_DIR_FOUND ON)
85 | elseif (NOT WIN32 AND EXISTS "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}")
86 | set (OpenCASCADE_LIBRARY_DIR_FOUND ON)
87 | set (OpenCASCADE_IMPLIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
88 | endif()
89 |
90 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_STATIC_LIBRARY_SUFFIX}")
91 | set (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND ON)
92 | elseif (NOT WIN32 AND EXISTS "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}")
93 | set (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND ON)
94 | set (OpenCASCADE_IMPLIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
95 | elseif (OpenCASCADE_LIBRARY_DIR_FOUND)
96 | message (STATUS "Only release OpenCASCADE libraries have been found")
97 | endif()
98 |
99 | if (NOT OpenCASCADE_LIBRARY_DIR_FOUND AND OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND)
100 | set (OpenCASCADE_LIBRARY_DIR_FOUND ON)
101 | message (WARNING "Only debug OpenCASCADE libraries have been found")
102 | endif()
103 |
104 | if (WIN32)
105 | if (EXISTS "${OpenCASCADE_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}")
106 | set (OpenCASCADE_SHAREDLIB_RELEASE_FOUND ON)
107 | endif()
108 | if (EXISTS "${OpenCASCADE_BINARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}")
109 | set (OpenCASCADE_SHAREDLIB_DEBUG_FOUND ON)
110 | endif()
111 | else()
112 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}")
113 | set (OpenCASCADE_SHAREDLIB_RELEASE_FOUND ON)
114 | endif()
115 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}")
116 | set (OpenCASCADE_SHAREDLIB_DEBUG_FOUND ON)
117 | endif()
118 | endif()
119 |
120 | if (OpenCASCADE_INCLUDE_DIR_FOUND AND OpenCASCADE_LIBRARY_DIR_FOUND)
121 | set (OpenCASCADE_FOUND ON)
122 | set (OpenCASCADE_INSTALL_PREFIX ${OpenCASCADE_DIR})
123 |
124 | # Define OCCT toolkits so that CMake can put absolute paths to linker;
125 | # the library existence is not checked here, since modules can be disabled.
126 | foreach (aLibIter ${OpenCASCADE_TKLIST})
127 | add_library (${aLibIter} SHARED IMPORTED)
128 |
129 | set_property (TARGET ${aLibIter} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
130 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_IMPLIB_RELEASE "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${OpenCASCADE_IMPLIB_SUFFIX}")
131 | if (OpenCASCADE_SHAREDLIB_RELEASE_FOUND)
132 | if (WIN32)
133 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_RELEASE "${OpenCASCADE_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}")
134 | else()
135 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_RELEASE "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}")
136 | endif()
137 | endif()
138 |
139 | if (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND)
140 | set_property (TARGET ${aLibIter} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
141 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_IMPLIB_DEBUG "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${OpenCASCADE_IMPLIB_SUFFIX}")
142 | if (OpenCASCADE_SHAREDLIB_DEBUG_FOUND)
143 | if (WIN32)
144 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_DEBUG "${OpenCASCADE_BINARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}")
145 | else()
146 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_DEBUG "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}")
147 | endif()
148 | endif()
149 | endif()
150 | endforeach()
151 | else()
152 | # fallback searching for CMake configs
153 | if (NOT "${OpenCASCADE_DIR}" STREQUAL "")
154 | set (anOcctDirBak "${OpenCASCADE_DIR}")
155 | find_package (OpenCASCADE CONFIG QUIET PATHS "${OpenCASCADE_DIR}" NO_DEFAULT_PATH)
156 | set (OpenCASCADE_DIR "${anOcctDirBak}" CACHE PATH "Path to Open CASCADE libraries." FORCE)
157 | else()
158 | find_package (OpenCASCADE CONFIG QUIET)
159 | endif()
160 | endif()
161 |
--------------------------------------------------------------------------------
/occt-qwidget/OcctQWidgetViewer.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #ifdef _WIN32
4 | // should be included before other headers to avoid missing definitions
5 | #include
6 | #endif
7 |
8 | #include "OcctQWidgetViewer.h"
9 |
10 | #include "../occt-qt-tools/OcctQtTools.h"
11 | #include "../occt-qt-tools/OcctGlTools.h"
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #if !defined(__APPLE__) && !defined(_WIN32) && defined(__has_include)
27 | #if __has_include()
28 | #include
29 | #define USE_XW_DISPLAY
30 | #endif
31 | #endif
32 | #ifndef USE_XW_DISPLAY
33 | typedef Aspect_DisplayConnection Xw_DisplayConnection;
34 | #endif
35 |
36 | // ================================================================
37 | // Function : OcctQWidgetViewer
38 | // ================================================================
39 | OcctQWidgetViewer::OcctQWidgetViewer(QWidget* theParent)
40 | : QWidget(theParent)
41 | {
42 | Handle(Aspect_DisplayConnection) aDisp = new Xw_DisplayConnection();
43 | Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver(aDisp, false);
44 |
45 | // create viewer
46 | myViewer = new V3d_Viewer(aDriver);
47 | myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK);
48 | myViewer->SetDefaultLights();
49 | myViewer->SetLightOn();
50 | myViewer->ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines);
51 |
52 | // create AIS context
53 | myContext = new AIS_InteractiveContext(myViewer);
54 |
55 | myViewCube = new AIS_ViewCube();
56 | myViewCube->SetViewAnimation(myViewAnimation);
57 | myViewCube->SetFixedAnimationLoop(false);
58 | myViewCube->SetAutoStartAnimation(true);
59 | myViewCube->TransformPersistence()->SetOffset2d(Graphic3d_Vec2i(100, 150));
60 |
61 | // note - window will be created later within initializeGL() callback!
62 | myView = myViewer->CreateView();
63 | myView->SetImmediateUpdate(false);
64 | #ifndef __APPLE__
65 | myView->ChangeRenderingParams().NbMsaaSamples = 4; // warning - affects performance
66 | #endif
67 | myView->ChangeRenderingParams().ToShowStats = true;
68 | // NOLINTNEXTLINE
69 | myView->ChangeRenderingParams().CollectedStats = (Graphic3d_RenderingParams::PerfCounters)(
70 | Graphic3d_RenderingParams::PerfCounters_FrameRate | Graphic3d_RenderingParams::PerfCounters_Triangles);
71 |
72 | // Qt widget setup
73 | setAttribute(Qt::WA_PaintOnScreen);
74 | setAttribute(Qt::WA_NoSystemBackground);
75 | setAttribute(Qt::WA_NativeWindow); // request native window for this widget to create OpenGL context
76 | setAttribute(Qt::WA_AcceptTouchEvents); // necessary to receive QTouchEvent events
77 | setMouseTracking(true);
78 | setBackgroundRole(QPalette::NoRole); // or NoBackground
79 | setFocusPolicy(Qt::StrongFocus); // set focus policy to threat QContextMenuEvent from keyboard
80 | setUpdatesEnabled(true);
81 |
82 | initializeGL();
83 | }
84 |
85 | // ================================================================
86 | // Function : ~OcctQWidgetViewer
87 | // ================================================================
88 | OcctQWidgetViewer::~OcctQWidgetViewer()
89 | {
90 | Handle(Aspect_DisplayConnection) aDisp = myViewer->Driver()->GetDisplayConnection();
91 |
92 | // release OCCT viewer
93 | myContext->RemoveAll(false);
94 | myContext.Nullify();
95 | myView->Remove();
96 | myView.Nullify();
97 | myViewer.Nullify();
98 |
99 | aDisp.Nullify();
100 | }
101 |
102 | // ================================================================
103 | // Function : event
104 | // ================================================================
105 | bool OcctQWidgetViewer::event(QEvent* theEvent)
106 | {
107 | if (myView.IsNull())
108 | return QWidget::event(theEvent);
109 |
110 | if (theEvent->type() == QEvent::TouchBegin
111 | || theEvent->type() == QEvent::TouchUpdate
112 | || theEvent->type() == QEvent::TouchEnd)
113 | {
114 | theEvent->accept();
115 | myHasTouchInput = true;
116 | if (OcctQtTools::qtHandleTouchEvent(*this, myView, static_cast(theEvent)))
117 | updateView();
118 |
119 | return true;
120 | }
121 |
122 | return QWidget::event(theEvent);
123 | }
124 |
125 | // ================================================================
126 | // Function : closeEvent
127 | // ================================================================
128 | void OcctQWidgetViewer::closeEvent(QCloseEvent* theEvent)
129 | {
130 | theEvent->accept();
131 | }
132 |
133 | // ================================================================
134 | // Function : keyPressEvent
135 | // ================================================================
136 | void OcctQWidgetViewer::keyPressEvent(QKeyEvent* theEvent)
137 | {
138 | if (myView.IsNull())
139 | return;
140 |
141 | const Aspect_VKey aKey = OcctQtTools::qtKey2VKey(theEvent->key());
142 | switch (aKey)
143 | {
144 | case Aspect_VKey_Escape:
145 | {
146 | QApplication::exit();
147 | return;
148 | }
149 | case Aspect_VKey_F:
150 | {
151 | myView->FitAll(0.01, false);
152 | update();
153 | theEvent->accept();
154 | return;
155 | }
156 | }
157 | QWidget::keyPressEvent(theEvent);
158 | }
159 |
160 | // ================================================================
161 | // Function : mousePressEvent
162 | // ================================================================
163 | void OcctQWidgetViewer::mousePressEvent(QMouseEvent* theEvent)
164 | {
165 | QWidget::mousePressEvent(theEvent);
166 | if (myView.IsNull())
167 | return;
168 |
169 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem)
170 | return; // skip mouse events emulated by system from screen touches
171 |
172 | theEvent->accept();
173 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
174 | updateView();
175 | }
176 |
177 | // ================================================================
178 | // Function : mouseReleaseEvent
179 | // ================================================================
180 | void OcctQWidgetViewer::mouseReleaseEvent(QMouseEvent* theEvent)
181 | {
182 | QWidget::mouseReleaseEvent(theEvent);
183 | if (myView.IsNull())
184 | return;
185 |
186 | theEvent->accept();
187 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
188 | updateView();
189 | }
190 |
191 | // ================================================================
192 | // Function : mouseMoveEvent
193 | // ================================================================
194 | void OcctQWidgetViewer::mouseMoveEvent(QMouseEvent* theEvent)
195 | {
196 | QWidget::mouseMoveEvent(theEvent);
197 | if (myView.IsNull())
198 | return;
199 |
200 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem)
201 | return; // skip mouse events emulated by system from screen touches
202 |
203 | theEvent->accept();
204 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
205 | updateView();
206 | }
207 |
208 | // ==============================================================================
209 | // function : wheelEvent
210 | // ==============================================================================
211 | void OcctQWidgetViewer::wheelEvent(QWheelEvent* theEvent)
212 | {
213 | QWidget::wheelEvent(theEvent);
214 | if (myView.IsNull())
215 | return;
216 |
217 | theEvent->accept();
218 | #if (OCC_VERSION_HEX >= 0x070700)
219 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
220 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y());
221 | #else
222 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y());
223 | #endif
224 | const Graphic3d_Vec2i aPnt2i(myView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5));
225 | if (!myView->Subviews().IsEmpty())
226 | {
227 | Handle(V3d_View) aPickedView = myView->PickSubview(aPnt2i);
228 | if (!aPickedView.IsNull() && aPickedView != myFocusView)
229 | {
230 | // switch input focus to another subview
231 | OnSubviewChanged(myContext, myFocusView, aPickedView);
232 | updateView();
233 | return;
234 | }
235 | }
236 | #endif
237 |
238 | if (OcctQtTools::qtHandleWheelEvent(*this, myView, theEvent))
239 | updateView();
240 | }
241 |
242 | // =======================================================================
243 | // function : updateView
244 | // =======================================================================
245 | void OcctQWidgetViewer::updateView()
246 | {
247 | QWidget::update();
248 | // if (window() != NULL) { window()->update(); }
249 | }
250 |
251 | // ================================================================
252 | // Function : resizeEvent
253 | // ================================================================
254 | void OcctQWidgetViewer::resizeEvent(QResizeEvent* )
255 | {
256 | if (!myView.IsNull())
257 | myView->MustBeResized();
258 | }
259 |
260 | // ================================================================
261 | // Function : handleViewRedraw
262 | // ================================================================
263 | void OcctQWidgetViewer::handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView)
264 | {
265 | AIS_ViewController::handleViewRedraw(theCtx, theView);
266 | if (myToAskNextFrame)
267 | updateView(); // ask more frames for animation
268 | }
269 |
270 | #if (OCC_VERSION_HEX >= 0x070700)
271 | // ================================================================
272 | // Function : OnSubviewChanged
273 | // ================================================================
274 | void OcctQWidgetViewer::OnSubviewChanged(const Handle(AIS_InteractiveContext)&,
275 | const Handle(V3d_View)&,
276 | const Handle(V3d_View)& theNewView)
277 | {
278 | myFocusView = theNewView;
279 | }
280 | #endif
281 |
282 | // ================================================================
283 | // Function : dumpGlInfo
284 | // ================================================================
285 | void OcctQWidgetViewer::dumpGlInfo(bool theIsBasic, bool theToPrint)
286 | {
287 | TColStd_IndexedDataMapOfStringString aGlCapsDict;
288 | myView->DiagnosticInformation(aGlCapsDict,
289 | theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete);
290 | TCollection_AsciiString anInfo;
291 | for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter(aGlCapsDict); aValueIter.More(); aValueIter.Next())
292 | {
293 | if (!aValueIter.Value().IsEmpty())
294 | {
295 | if (!anInfo.IsEmpty())
296 | anInfo += "\n";
297 |
298 | anInfo += aValueIter.Key() + ": " + aValueIter.Value();
299 | }
300 | }
301 |
302 | if (theToPrint)
303 | Message::SendInfo(anInfo);
304 |
305 | myGlInfo = QString::fromUtf8(anInfo.ToCString());
306 | }
307 |
308 | // ================================================================
309 | // Function : initializeGL
310 | // ================================================================
311 | void OcctQWidgetViewer::initializeGL()
312 | {
313 | const QRect aRect = rect();
314 | const double aDevPixRatio = devicePixelRatioF();
315 | const Graphic3d_Vec2i aViewSize(Graphic3d_Vec2d(Round((aRect.right() - aRect.left()) * aDevPixRatio),
316 | Round((aRect.bottom() - aRect.top()) * aDevPixRatio)));
317 |
318 | const Aspect_Drawable aNativeWin = (Aspect_Drawable)winId();
319 |
320 | Handle(OcctGlTools::OcctNeutralWindow) aWindow = Handle(OcctGlTools::OcctNeutralWindow)::DownCast(myView->Window());
321 | const bool isFirstInit = aWindow.IsNull();
322 | if (aWindow.IsNull())
323 | {
324 | aWindow = new OcctGlTools::OcctNeutralWindow();
325 | aWindow->SetVirtual(true);
326 | }
327 | aWindow->SetNativeHandle(aNativeWin);
328 | aWindow->SetSize(aViewSize.x(), aViewSize.y());
329 | aWindow->SetDevicePixelRatio(aDevPixRatio);
330 | myView->SetWindow(aWindow);
331 | dumpGlInfo(true, true);
332 | #if (OCC_VERSION_HEX >= 0x070700)
333 | for (const Handle(V3d_View)& aSubviewIter : myView->Subviews())
334 | {
335 | aSubviewIter->MustBeResized();
336 | aSubviewIter->Invalidate();
337 | }
338 | #endif
339 |
340 | if (isFirstInit)
341 | {
342 | myContext->Display(myViewCube, 0, 0, false);
343 |
344 | // dummy shape for testing
345 | TopoDS_Shape aBox = BRepPrimAPI_MakeBox(100.0, 50.0, 90.0).Shape();
346 | Handle(AIS_Shape) aShape = new AIS_Shape(aBox);
347 | myContext->Display(aShape, AIS_Shaded, 0, false);
348 | }
349 | }
350 |
351 | // ================================================================
352 | // Function : paintEvent
353 | // ================================================================
354 | void OcctQWidgetViewer::paintEvent(QPaintEvent* )
355 | {
356 | if (myView.IsNull() || myView->Window().IsNull())
357 | return;
358 |
359 | const double aDevPixelRatioOld = myView->Window()->DevicePixelRatio();
360 | const double aDevPixelRatioNew = devicePixelRatioF();
361 | if (myView->Window()->NativeHandle() != (Aspect_Drawable)winId())
362 | {
363 | // workaround window recreation done by Qt on monitor (QScreen) disconnection
364 | Message::SendWarning() << "Native window handle has changed by QWidget!";
365 | initializeGL();
366 | }
367 | else if (aDevPixelRatioNew != aDevPixelRatioOld)
368 | {
369 | initializeGL();
370 | }
371 | else
372 | {
373 | Graphic3d_Vec2i aViewSizeOld; myView->Window()->Size(aViewSizeOld.x(), aViewSizeOld.y());
374 |
375 | const QRect aRect = rect();
376 | Graphic3d_Vec2i aViewSizeNew(Graphic3d_Vec2d(Round((aRect.right() - aRect.left()) * aDevPixelRatioNew),
377 | Round((aRect.bottom() - aRect.top()) * aDevPixelRatioNew)));
378 | if (aViewSizeNew != aViewSizeOld)
379 | {
380 | Handle(OcctGlTools::OcctNeutralWindow) aWindow = Handle(OcctGlTools::OcctNeutralWindow)::DownCast(myView->Window());
381 | aWindow->SetSize(aViewSizeNew.x(), aViewSizeNew.y());
382 | myView->MustBeResized();
383 | myView->Invalidate();
384 | dumpGlInfo(true, false);
385 |
386 | #if (OCC_VERSION_HEX >= 0x070700)
387 | for (const Handle(V3d_View)& aSubviewIter : myView->Subviews())
388 | {
389 | aSubviewIter->MustBeResized();
390 | aSubviewIter->Invalidate();
391 | }
392 | #endif
393 | }
394 | }
395 |
396 | // flush pending input events and redraw the viewer
397 | Handle(V3d_View) aView = !myFocusView.IsNull() ? myFocusView : myView;
398 | aView->InvalidateImmediate();
399 | AIS_ViewController::FlushViewEvents(myContext, aView, true);
400 | }
401 |
--------------------------------------------------------------------------------
/occt-qopenglwidget/OcctQOpenGLWidgetViewer.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023 Kirill Gavrilov
2 |
3 | #ifdef _WIN32
4 | // should be included before other headers to avoid missing definitions
5 | #include
6 | #endif
7 | #include
8 |
9 | #include "OcctQOpenGLWidgetViewer.h"
10 |
11 | #include "../occt-qt-tools/OcctGlTools.h"
12 | #include "../occt-qt-tools/OcctQtTools.h"
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #if !defined(__APPLE__) && !defined(_WIN32) && defined(__has_include)
28 | #if __has_include()
29 | #include
30 | #define USE_XW_DISPLAY
31 | #endif
32 | #endif
33 | #ifndef USE_XW_DISPLAY
34 | typedef Aspect_DisplayConnection Xw_DisplayConnection;
35 | #endif
36 |
37 | // ================================================================
38 | // Function : OcctQOpenGLWidgetViewer
39 | // ================================================================
40 | OcctQOpenGLWidgetViewer::OcctQOpenGLWidgetViewer(QWidget* theParent)
41 | : QOpenGLWidget(theParent)
42 | {
43 | Handle(Aspect_DisplayConnection) aDisp = new Xw_DisplayConnection();
44 | Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver(aDisp, false);
45 | // lets QOpenGLWidget to manage buffer swap
46 | aDriver->ChangeOptions().buffersNoSwap = true;
47 | // don't write into alpha channel
48 | aDriver->ChangeOptions().buffersOpaqueAlpha = true;
49 | // offscreen FBOs should be always used
50 | aDriver->ChangeOptions().useSystemBuffer = false;
51 |
52 | // create viewer
53 | myViewer = new V3d_Viewer(aDriver);
54 | myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK);
55 | myViewer->SetDefaultLights();
56 | myViewer->SetLightOn();
57 | myViewer->ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines);
58 |
59 | // create AIS context
60 | myContext = new AIS_InteractiveContext(myViewer);
61 |
62 | myViewCube = new AIS_ViewCube();
63 | myViewCube->SetViewAnimation(myViewAnimation);
64 | myViewCube->SetFixedAnimationLoop(false);
65 | myViewCube->SetAutoStartAnimation(true);
66 | myViewCube->TransformPersistence()->SetOffset2d(Graphic3d_Vec2i(100, 150));
67 |
68 | // note - window will be created later within initializeGL() callback!
69 | myView = myViewer->CreateView();
70 | myView->SetImmediateUpdate(false);
71 | #ifndef __APPLE__
72 | myView->ChangeRenderingParams().NbMsaaSamples = 4; // warning - affects performance
73 | #endif
74 | myView->ChangeRenderingParams().ToShowStats = true;
75 | // NOLINTNEXTLINE
76 | myView->ChangeRenderingParams().CollectedStats = (Graphic3d_RenderingParams::PerfCounters)(
77 | Graphic3d_RenderingParams::PerfCounters_FrameRate | Graphic3d_RenderingParams::PerfCounters_Triangles);
78 |
79 | // Qt widget setup
80 | setAttribute(Qt::WA_AcceptTouchEvents); // necessary to receive QTouchEvent events
81 | setMouseTracking(true);
82 | setBackgroundRole(QPalette::NoRole); // or NoBackground
83 | setFocusPolicy(Qt::StrongFocus); // set focus policy to threat QContextMenuEvent from keyboard
84 | setUpdatesEnabled(true);
85 | setUpdateBehavior(QOpenGLWidget::NoPartialUpdate);
86 |
87 | // OpenGL setup managed by Qt - it is better to do this globally
88 | // via QSurfaceFormat::setDefaultFormat() - see main() function
89 | //const QSurfaceFormat aGlFormat = OcctQtTools::qtGlSurfaceFormat();
90 | //setFormat(aGlFormat);
91 | }
92 |
93 | // ================================================================
94 | // Function : ~OcctQOpenGLWidgetViewer
95 | // ================================================================
96 | OcctQOpenGLWidgetViewer::~OcctQOpenGLWidgetViewer()
97 | {
98 | // hold on X11 display connection till making another connection active by glXMakeCurrent()
99 | // to workaround sudden crash in QOpenGLWidget destructor
100 | Handle(Aspect_DisplayConnection) aDisp = myViewer->Driver()->GetDisplayConnection();
101 |
102 | // release OCCT viewer
103 | myContext->RemoveAll(false);
104 | myContext.Nullify();
105 | myView->Remove();
106 | myView.Nullify();
107 | myViewer.Nullify();
108 |
109 | // make active OpenGL context created by Qt
110 | makeCurrent();
111 | aDisp.Nullify();
112 | }
113 |
114 | // ================================================================
115 | // Function : event
116 | // ================================================================
117 | bool OcctQOpenGLWidgetViewer::event(QEvent* theEvent)
118 | {
119 | if (myView.IsNull())
120 | return QOpenGLWidget::event(theEvent);
121 |
122 | if (theEvent->type() == QEvent::TouchBegin
123 | || theEvent->type() == QEvent::TouchUpdate
124 | || theEvent->type() == QEvent::TouchEnd)
125 | {
126 | theEvent->accept();
127 | myHasTouchInput = true;
128 | if (OcctQtTools::qtHandleTouchEvent(*this, myView, static_cast(theEvent)))
129 | updateView();
130 |
131 | return true;
132 | }
133 |
134 | return QOpenGLWidget::event(theEvent);
135 | }
136 |
137 | // ================================================================
138 | // Function : closeEvent
139 | // ================================================================
140 | void OcctQOpenGLWidgetViewer::closeEvent(QCloseEvent* theEvent)
141 | {
142 | theEvent->accept();
143 | }
144 |
145 | // ================================================================
146 | // Function : keyPressEvent
147 | // ================================================================
148 | void OcctQOpenGLWidgetViewer::keyPressEvent(QKeyEvent* theEvent)
149 | {
150 | if (myView.IsNull())
151 | return;
152 |
153 | const Aspect_VKey aKey = OcctQtTools::qtKey2VKey(theEvent->key());
154 | switch (aKey)
155 | {
156 | case Aspect_VKey_Escape: {
157 | QApplication::exit();
158 | return;
159 | }
160 | case Aspect_VKey_F: {
161 | myView->FitAll(0.01, false);
162 | update();
163 | theEvent->accept();
164 | return;
165 | }
166 | }
167 | QOpenGLWidget::keyPressEvent(theEvent);
168 | }
169 |
170 | // ================================================================
171 | // Function : mousePressEvent
172 | // ================================================================
173 | void OcctQOpenGLWidgetViewer::mousePressEvent(QMouseEvent* theEvent)
174 | {
175 | QOpenGLWidget::mousePressEvent(theEvent);
176 | if (myView.IsNull())
177 | return;
178 |
179 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem)
180 | return; // skip mouse events emulated by system from screen touches
181 |
182 | theEvent->accept();
183 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
184 | updateView();
185 | }
186 |
187 | // ================================================================
188 | // Function : mouseReleaseEvent
189 | // ================================================================
190 | void OcctQOpenGLWidgetViewer::mouseReleaseEvent(QMouseEvent* theEvent)
191 | {
192 | QOpenGLWidget::mouseReleaseEvent(theEvent);
193 | if (myView.IsNull())
194 | return;
195 |
196 | theEvent->accept();
197 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
198 | updateView();
199 | }
200 |
201 | // ================================================================
202 | // Function : mouseMoveEvent
203 | // ================================================================
204 | void OcctQOpenGLWidgetViewer::mouseMoveEvent(QMouseEvent* theEvent)
205 | {
206 | QOpenGLWidget::mouseMoveEvent(theEvent);
207 | if (myView.IsNull())
208 | return;
209 |
210 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem)
211 | return; // skip mouse events emulated by system from screen touches
212 |
213 | theEvent->accept();
214 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
215 | updateView();
216 | }
217 |
218 | // ==============================================================================
219 | // function : wheelEvent
220 | // ==============================================================================
221 | void OcctQOpenGLWidgetViewer::wheelEvent(QWheelEvent* theEvent)
222 | {
223 | QOpenGLWidget::wheelEvent(theEvent);
224 | if (myView.IsNull())
225 | return;
226 |
227 | theEvent->accept();
228 |
229 | #if (OCC_VERSION_HEX >= 0x070700)
230 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
231 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y());
232 | #else
233 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y());
234 | #endif
235 | const Graphic3d_Vec2i aPnt2i(myView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5));
236 | if (!myView->Subviews().IsEmpty())
237 | {
238 | Handle(V3d_View) aPickedView = myView->PickSubview(aPnt2i);
239 | if (!aPickedView.IsNull() && aPickedView != myFocusView)
240 | {
241 | // switch input focus to another subview
242 | OnSubviewChanged(myContext, myFocusView, aPickedView);
243 | updateView();
244 | return;
245 | }
246 | }
247 | #endif
248 |
249 | if (OcctQtTools::qtHandleWheelEvent(*this, myView, theEvent))
250 | updateView();
251 | }
252 |
253 | // =======================================================================
254 | // function : updateView
255 | // =======================================================================
256 | void OcctQOpenGLWidgetViewer::updateView()
257 | {
258 | update();
259 | // if (window() != NULL) { window()->update(); }
260 | }
261 |
262 | // ================================================================
263 | // Function : handleViewRedraw
264 | // ================================================================
265 | void OcctQOpenGLWidgetViewer::handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx,
266 | const Handle(V3d_View)& theView)
267 | {
268 | AIS_ViewController::handleViewRedraw(theCtx, theView);
269 | if (myToAskNextFrame)
270 | updateView(); // ask more frames for animation
271 | }
272 |
273 | #if (OCC_VERSION_HEX >= 0x070700)
274 | // ================================================================
275 | // Function : OnSubviewChanged
276 | // ================================================================
277 | void OcctQOpenGLWidgetViewer::OnSubviewChanged(const Handle(AIS_InteractiveContext)&,
278 | const Handle(V3d_View)&,
279 | const Handle(V3d_View)& theNewView)
280 | {
281 | myFocusView = theNewView;
282 | }
283 | #endif
284 |
285 | // ================================================================
286 | // Function : dumpGlInfo
287 | // ================================================================
288 | void OcctQOpenGLWidgetViewer::dumpGlInfo(bool theIsBasic, bool theToPrint)
289 | {
290 | TColStd_IndexedDataMapOfStringString aGlCapsDict;
291 | myView->DiagnosticInformation(aGlCapsDict,
292 | theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete);
293 | TCollection_AsciiString anInfo;
294 | for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter(aGlCapsDict); aValueIter.More(); aValueIter.Next())
295 | {
296 | if (!aValueIter.Value().IsEmpty())
297 | {
298 | if (!anInfo.IsEmpty())
299 | anInfo += "\n";
300 |
301 | anInfo += aValueIter.Key() + ": " + aValueIter.Value();
302 | }
303 | }
304 |
305 | if (theToPrint)
306 | Message::SendInfo(anInfo);
307 |
308 | myGlInfo = QString::fromUtf8(anInfo.ToCString());
309 | }
310 |
311 | // ================================================================
312 | // Function : initializeGL
313 | // ================================================================
314 | void OcctQOpenGLWidgetViewer::initializeGL()
315 | {
316 | Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast(myViewer->Driver());
317 | OcctQtTools::qtGlCapsFromSurfaceFormat(aDriver->ChangeOptions(), format());
318 |
319 | const Aspect_Drawable aNativeWin = (Aspect_Drawable)effectiveWinId();
320 | const Graphic3d_Vec2i aViewSize(rect().right() - rect().left(), rect().bottom() - rect().top());
321 |
322 | const bool isFirstInit = myView->Window().IsNull();
323 | if (!OcctGlTools::InitializeGlWindow(myView, aNativeWin, aViewSize, devicePixelRatioF()))
324 | {
325 | QMessageBox::critical(0, "Failure", "OpenGl_Context is unable to wrap OpenGL context");
326 | QApplication::exit(1);
327 | return;
328 | }
329 |
330 | makeCurrent(); // restore Qt framebuffer
331 | dumpGlInfo(true, true);
332 | if (isFirstInit)
333 | {
334 | myContext->Display(myViewCube, 0, 0, false);
335 |
336 | // dummy shape for testing
337 | TopoDS_Shape aBox = BRepPrimAPI_MakeBox(100.0, 50.0, 90.0).Shape();
338 | Handle(AIS_Shape) aShape = new AIS_Shape(aBox);
339 | myContext->Display(aShape, AIS_Shaded, 0, false);
340 | }
341 | }
342 |
343 | // ================================================================
344 | // Function : paintGL
345 | // ================================================================
346 | void OcctQOpenGLWidgetViewer::paintGL()
347 | {
348 | if (myView.IsNull() || myView->Window().IsNull())
349 | return;
350 |
351 | const double aDevPixelRatioOld = myView->Window()->DevicePixelRatio();
352 | if (myView->Window()->NativeHandle() != OcctGlTools::GetGlNativeWindow((Aspect_Drawable)effectiveWinId()))
353 | {
354 | // workaround window recreation done by Qt on monitor (QScreen) disconnection
355 | Message::SendWarning() << "Native window handle has changed by QOpenGLWidget!";
356 | initializeGL();
357 | }
358 | else if (devicePixelRatioF() != aDevPixelRatioOld)
359 | {
360 | initializeGL();
361 | }
362 |
363 | Graphic3d_Vec2i aViewSizeOld; myView->Window()->Size(aViewSizeOld.x(), aViewSizeOld.y());
364 |
365 | // wrap FBO created by QOpenGLFramebufferObject
366 | if (!OcctGlTools::InitializeGlFbo(myView))
367 | {
368 | QMessageBox::critical(0, "Failure", "Default FBO wrapper creation failed");
369 | QApplication::exit(1);
370 | return;
371 | }
372 |
373 | Graphic3d_Vec2i aViewSizeNew; myView->Window()->Size(aViewSizeNew.x(), aViewSizeNew.y());
374 | if (aViewSizeNew != aViewSizeOld || myView->Window()->DevicePixelRatio() != aDevPixelRatioOld)
375 | dumpGlInfo(true, false);
376 |
377 | // reset global GL state from Qt before redrawing OCCT
378 | OcctGlTools::ResetGlStateBeforeOcct(myView);
379 |
380 | // flush pending input events and redraw the viewer
381 | Handle(V3d_View) aView = !myFocusView.IsNull() ? myFocusView : myView;
382 | aView->InvalidateImmediate();
383 | AIS_ViewController::FlushViewEvents(myContext, aView, true);
384 |
385 | // reset global GL state after OCCT before redrawing Qt
386 | OcctGlTools::ResetGlStateAfterOcct(myView);
387 | }
388 |
--------------------------------------------------------------------------------
/occt-qtquick/OcctQQuickFramebufferViewer.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023 Kirill Gavrilov
2 |
3 | #ifdef _WIN32
4 | // should be included before other headers to avoid missing definitions
5 | #include
6 | #endif
7 | #include
8 |
9 | #include "OcctQQuickFramebufferViewer.h"
10 |
11 | #include "../occt-qt-tools/OcctGlTools.h"
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | #if !defined(__APPLE__) && !defined(_WIN32) && defined(__has_include)
29 | #if __has_include()
30 | #include
31 | #define USE_XW_DISPLAY
32 | #endif
33 | #endif
34 | #ifndef USE_XW_DISPLAY
35 | typedef Aspect_DisplayConnection Xw_DisplayConnection;
36 | #endif
37 |
38 | // ================================================================
39 | // Function : OcctQQuickFramebufferViewer
40 | // ================================================================
41 | OcctQQuickFramebufferViewer::OcctQQuickFramebufferViewer(QQuickItem* theParent)
42 | : QQuickFramebufferObject(theParent)
43 | {
44 | Handle(Aspect_DisplayConnection) aDisp = new Xw_DisplayConnection();
45 | Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver(aDisp, false);
46 | // lets QtQuick to manage buffer swap
47 | aDriver->ChangeOptions().buffersNoSwap = true;
48 | // don't write into alpha channel
49 | aDriver->ChangeOptions().buffersOpaqueAlpha = true;
50 | // offscreen FBOs should be always used
51 | aDriver->ChangeOptions().useSystemBuffer = false;
52 |
53 | // create viewer
54 | myViewer = new V3d_Viewer(aDriver);
55 | myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK);
56 | myViewer->SetDefaultLights();
57 | myViewer->SetLightOn();
58 | myViewer->ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines);
59 |
60 | // create AIS context
61 | myContext = new AIS_InteractiveContext(myViewer);
62 |
63 | myViewCube = new AIS_ViewCube();
64 | myViewCube->SetViewAnimation(myViewAnimation);
65 | myViewCube->SetFixedAnimationLoop(false);
66 | myViewCube->SetAutoStartAnimation(true);
67 | myViewCube->TransformPersistence()->SetOffset2d(Graphic3d_Vec2i(100, 150));
68 |
69 | // note - window will be created later within initializeGL() callback!
70 | myView = myViewer->CreateView();
71 | myView->SetImmediateUpdate(false);
72 | #ifndef __APPLE__
73 | myView->ChangeRenderingParams().NbMsaaSamples = 4; // warning - affects performance
74 | #endif
75 | myView->ChangeRenderingParams().ToShowStats = true;
76 | // NOLINTNEXTLINE
77 | myView->ChangeRenderingParams().CollectedStats = (Graphic3d_RenderingParams::PerfCounters)(
78 | Graphic3d_RenderingParams::PerfCounters_FrameRate | Graphic3d_RenderingParams::PerfCounters_Triangles);
79 |
80 | // QtQuick item setup
81 | setAcceptedMouseButtons(Qt::AllButtons);
82 | //setAcceptTouchEvents(true); // necessary to receive QTouchEvent events
83 | setAcceptHoverEvents(true);
84 | setMirrorVertically(true);
85 |
86 | // GUI elements cannot be created from GL rendering thread - make queued connection
87 | connect(this, &OcctQQuickFramebufferViewer::glCriticalError, this, [this](QString theMsg)
88 | {
89 | QMessageBox::critical(0, "Critical error", theMsg);
90 | QApplication::exit(1);
91 | }, Qt::QueuedConnection);
92 | }
93 |
94 | // ================================================================
95 | // Function : ~OcctQQuickFramebufferViewer
96 | // ================================================================
97 | OcctQQuickFramebufferViewer::~OcctQQuickFramebufferViewer()
98 | {
99 | // hold on X11 display connection till making another connection active by glXMakeCurrent()
100 | // to workaround sudden crash in QOpenGLWidget destructor
101 | Handle(Aspect_DisplayConnection) aDisp = myViewer->Driver()->GetDisplayConnection();
102 |
103 | // release OCCT viewer
104 | myContext->RemoveAll(false);
105 | myContext.Nullify();
106 | myView->Remove();
107 | myView.Nullify();
108 | myViewer.Nullify();
109 |
110 | // make active OpenGL context created by Qt
111 | //makeCurrent();
112 | aDisp.Nullify();
113 | }
114 |
115 | // ================================================================
116 | // Function : event
117 | // ================================================================
118 | bool OcctQQuickFramebufferViewer::event(QEvent* theEvent)
119 | {
120 | if (myView.IsNull())
121 | return QQuickFramebufferObject::event(theEvent);
122 |
123 | if (theEvent->type() == QEvent::UpdateLater)
124 | {
125 | update();
126 | theEvent->accept();
127 | return true;
128 | }
129 |
130 | if (theEvent->type() == QEvent::TouchBegin
131 | || theEvent->type() == QEvent::TouchUpdate
132 | || theEvent->type() == QEvent::TouchEnd)
133 | {
134 | theEvent->accept();
135 | myHasTouchInput = true;
136 | if (OcctQtTools::qtHandleTouchEvent(*this, myView, static_cast(theEvent)))
137 | updateView();
138 |
139 | return true;
140 | }
141 |
142 | return QQuickFramebufferObject::event(theEvent);
143 | }
144 |
145 | // ================================================================
146 | // Function : keyPressEvent
147 | // ================================================================
148 | void OcctQQuickFramebufferViewer::keyPressEvent(QKeyEvent* theEvent)
149 | {
150 | if (myView.IsNull())
151 | return;
152 |
153 | const Aspect_VKey aKey = OcctQtTools::qtKey2VKey(theEvent->key());
154 | switch (aKey)
155 | {
156 | case Aspect_VKey_Escape:
157 | {
158 | QApplication::exit();
159 | return;
160 | }
161 | case Aspect_VKey_F:
162 | {
163 | {
164 | Standard_Mutex::Sentry aLock(myViewerMutex);
165 | myView->FitAll(0.01, false);
166 | }
167 | update();
168 | theEvent->accept();
169 | return;
170 | }
171 | }
172 | QQuickFramebufferObject::keyPressEvent(theEvent);
173 | }
174 |
175 | // ================================================================
176 | // Function : mousePressEvent
177 | // ================================================================
178 | void OcctQQuickFramebufferViewer::mousePressEvent(QMouseEvent* theEvent)
179 | {
180 | QQuickFramebufferObject::mousePressEvent(theEvent);
181 | if (myView.IsNull())
182 | return;
183 |
184 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem)
185 | return; // skip mouse events emulated by system from screen touches
186 |
187 | theEvent->accept();
188 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
189 | updateView();
190 | }
191 |
192 | // ================================================================
193 | // Function : mouseReleaseEvent
194 | // ================================================================
195 | void OcctQQuickFramebufferViewer::mouseReleaseEvent(QMouseEvent* theEvent)
196 | {
197 | QQuickFramebufferObject::mouseReleaseEvent(theEvent);
198 | if (myView.IsNull())
199 | return;
200 |
201 | theEvent->accept();
202 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
203 | updateView();
204 |
205 | // take keyboard focus on mouse click
206 | setFocus(true);
207 | }
208 |
209 | // ================================================================
210 | // Function : mouseMoveEvent
211 | // ================================================================
212 | void OcctQQuickFramebufferViewer::mouseMoveEvent(QMouseEvent* theEvent)
213 | {
214 | QQuickFramebufferObject::mouseMoveEvent(theEvent);
215 | if (myView.IsNull())
216 | return;
217 |
218 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem)
219 | return; // skip mouse events emulated by system from screen touches
220 |
221 | theEvent->accept();
222 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent))
223 | updateView();
224 | }
225 |
226 | // ==============================================================================
227 | // function : wheelEvent
228 | // ==============================================================================
229 | void OcctQQuickFramebufferViewer::wheelEvent(QWheelEvent* theEvent)
230 | {
231 | QQuickFramebufferObject::wheelEvent(theEvent);
232 | if (myView.IsNull())
233 | return;
234 |
235 | theEvent->accept();
236 | if (OcctQtTools::qtHandleWheelEvent(*this, myView, theEvent))
237 | updateView();
238 | }
239 |
240 | // ================================================================
241 | // Function : hoverMoveEvent
242 | // ================================================================
243 | void OcctQQuickFramebufferViewer::hoverEnterEvent(QHoverEvent* theEvent)
244 | {
245 | QQuickFramebufferObject::hoverEnterEvent(theEvent);
246 | if (myView.IsNull())
247 | return;
248 |
249 | theEvent->accept();
250 | }
251 |
252 | // ================================================================
253 | // Function : hoverMoveEvent
254 | // ================================================================
255 | void OcctQQuickFramebufferViewer::hoverLeaveEvent(QHoverEvent* theEvent)
256 | {
257 | QQuickFramebufferObject::hoverLeaveEvent(theEvent);
258 | if (myView.IsNull())
259 | return;
260 |
261 | theEvent->accept();
262 | }
263 |
264 | // ================================================================
265 | // Function : hoverMoveEvent
266 | // ================================================================
267 | void OcctQQuickFramebufferViewer::hoverMoveEvent(QHoverEvent* theEvent)
268 | {
269 | QQuickFramebufferObject::hoverMoveEvent(theEvent);
270 | if (myView.IsNull())
271 | return;
272 |
273 | theEvent->accept();
274 | if (OcctQtTools::qtHandleHoverEvent(*this, myView, theEvent))
275 | updateView();
276 | }
277 |
278 | // =======================================================================
279 | // Function : updateView
280 | // =======================================================================
281 | void OcctQQuickFramebufferViewer::updateView()
282 | {
283 | update();
284 | }
285 |
286 | // ================================================================
287 | // Function : handleViewRedraw
288 | // ================================================================
289 | void OcctQQuickFramebufferViewer::handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx,
290 | const Handle(V3d_View)& theView)
291 | {
292 | AIS_ViewController::handleViewRedraw(theCtx, theView);
293 | if (myToAskNextFrame)
294 | QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateLater)); // ask more frames for animation
295 | }
296 |
297 | // =======================================================================
298 | // Function : createRenderer
299 | // =======================================================================
300 | QQuickFramebufferObject::Renderer* OcctQQuickFramebufferViewer::createRenderer() const
301 | {
302 | return new Renderer(const_cast(this));
303 | }
304 |
305 | // ================================================================
306 | // Function : Renderer
307 | // ================================================================
308 | OcctQQuickFramebufferViewer::Renderer::Renderer(OcctQQuickFramebufferViewer* theViewer)
309 | : myViewer(theViewer)
310 | {
311 | //
312 | }
313 |
314 | // ================================================================
315 | // Function : ~Renderer
316 | // ================================================================
317 | OcctQQuickFramebufferViewer::Renderer::~Renderer()
318 | {
319 | //
320 | }
321 |
322 | // ================================================================
323 | // Function : createFramebufferObject
324 | // ================================================================
325 | QOpenGLFramebufferObject* OcctQQuickFramebufferViewer::Renderer::createFramebufferObject(const QSize& theSize)
326 | {
327 | QOpenGLFramebufferObjectFormat aQFormat;
328 | aQFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
329 | //aQFormat.setInternalTextureFormat(GL_RGBA8);
330 | //aQFormat.setSamples(4); do not create MSAA buffer here
331 |
332 | /*#if (QT_VERSION_MAJOR > 5) || (QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR >= 10)
333 | const QQuickWindow* aQWindow = window();
334 | const QSurfaceFormat aQtGlFormat = aQWindow->format();
335 | if (aQtGlFormat.colorSpace() == QSurfaceFormat::sRGBColorSpace)
336 | aQFormat.setInternalTextureFormat(GL_SRGB8_ALPHA8);
337 | #endif*/
338 |
339 | return new QOpenGLFramebufferObject(theSize, aQFormat);
340 | }
341 |
342 | // ================================================================
343 | // Function : synchronize
344 | // ================================================================
345 | void OcctQQuickFramebufferViewer::Renderer::synchronize(QQuickFramebufferObject* theItem)
346 | {
347 | if (myViewer != theItem)
348 | std::cerr << "Error: unexpected synchronize\n";
349 |
350 | if (myViewer != nullptr)
351 | myViewer->synchronize(framebufferObject());
352 | }
353 |
354 | // ================================================================
355 | // Function : render
356 | // ================================================================
357 | void OcctQQuickFramebufferViewer::Renderer::render()
358 | {
359 | if (myViewer != nullptr)
360 | myViewer->render(framebufferObject());
361 | }
362 |
363 | // ================================================================
364 | // Function : dumpGlInfo
365 | // ================================================================
366 | void OcctQQuickFramebufferViewer::dumpGlInfo(bool theIsBasic, bool theToPrint)
367 | {
368 | TColStd_IndexedDataMapOfStringString aGlCapsDict;
369 | myView->DiagnosticInformation(aGlCapsDict,
370 | theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete);
371 | TCollection_AsciiString anInfo;
372 | for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter(aGlCapsDict); aValueIter.More(); aValueIter.Next())
373 | {
374 | if (!aValueIter.Value().IsEmpty())
375 | {
376 | if (!anInfo.IsEmpty())
377 | anInfo += "\n";
378 |
379 | anInfo += aValueIter.Key() + ": " + aValueIter.Value();
380 | }
381 | }
382 |
383 | if (theToPrint)
384 | Message::SendInfo(anInfo);
385 |
386 | myGlInfo = QString::fromUtf8(anInfo.ToCString());
387 | Q_EMIT glInfoChanged();
388 | }
389 |
390 | // ================================================================
391 | // Function : synchronize
392 | // ================================================================
393 | void OcctQQuickFramebufferViewer::synchronize(QOpenGLFramebufferObject* )
394 | {
395 | // this method will be called from GL rendering thread while GUI thread is locked,
396 | // the place to synchronize GUI / GL rendering states
397 | if (myGlBackColor.first)
398 | {
399 | myGlBackColor.first = false;
400 | const Quantity_Color aColor = OcctQtTools::qtColorToOcct(myGlBackColor.second);
401 | myView->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical);
402 | myView->Invalidate();
403 | }
404 | }
405 |
406 | // ================================================================
407 | // Function : initializeGL
408 | // ================================================================
409 | void OcctQQuickFramebufferViewer::initializeGL(QOpenGLFramebufferObject* theFbo)
410 | {
411 | // handle OCCT 3D Viewer initialization
412 | Standard_Mutex::Sentry aLock(myViewerMutex);
413 | const QQuickWindow* aQWindow = window();
414 | if (theFbo == nullptr)
415 | return;
416 |
417 | Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast(myViewer->Driver());
418 | OcctQtTools::qtGlCapsFromSurfaceFormat(aDriver->ChangeOptions(), aQWindow->format());
419 |
420 | const Aspect_Drawable aNativeWin = aQWindow != nullptr ? (Aspect_Drawable)aQWindow->winId() : 0;
421 | const Graphic3d_Vec2i aViewSize(theFbo->size().width(), theFbo->size().height());
422 |
423 | const bool isFirstInit = myView->Window().IsNull();
424 | theFbo->bindDefault(); // initialize OpenGl_Context with default (window) FBO
425 | if (!OcctGlTools::InitializeGlWindow(myView, aNativeWin, aViewSize, aQWindow->devicePixelRatio()))
426 | {
427 | Q_EMIT glCriticalError("OpenGl_Context is unable to wrap OpenGL context");
428 | return;
429 | }
430 |
431 | theFbo->bind(); // rebind offscreen FBO
432 | dumpGlInfo(true, true);
433 | if (isFirstInit)
434 | {
435 | myContext->Display(myViewCube, 0, 0, false);
436 |
437 | // dummy shape for testing
438 | TopoDS_Shape aBox = BRepPrimAPI_MakeBox(100.0, 50.0, 90.0).Shape();
439 | Handle(AIS_Shape) aShape = new AIS_Shape(aBox);
440 | myContext->Display(aShape, AIS_Shaded, 0, false);
441 | }
442 | }
443 |
444 | // ================================================================
445 | // Function : render
446 | // ================================================================
447 | void OcctQQuickFramebufferViewer::render(QOpenGLFramebufferObject* theFbo)
448 | {
449 | // this method is called from GL rendering thread;
450 | // accessing GUI items is not allowed here!
451 | Standard_Mutex::Sentry aLock(myViewerMutex);
452 | const QQuickWindow* aQWindow = window();
453 | if (theFbo == nullptr || myView.IsNull() || aQWindow == nullptr)
454 | return;
455 |
456 | const Aspect_Drawable aNativeWin = OcctGlTools::GetGlNativeWindow((Aspect_Drawable)aQWindow->winId());
457 | if (myView->Window().IsNull()
458 | || myView->Window()->NativeHandle() != aNativeWin)
459 | {
460 | initializeGL(theFbo);
461 | if (myView->Window().IsNull())
462 | return;
463 | }
464 |
465 | Graphic3d_Vec2i aViewSizeOld; myView->Window()->Size(aViewSizeOld.x(), aViewSizeOld.y());
466 | const double aDevPixelRatioOld = myView->Window()->DevicePixelRatio();
467 | if (aDevPixelRatioOld != aQWindow->devicePixelRatio())
468 | initializeGL(theFbo);
469 |
470 | // wrap FBO created by QOpenGLFramebufferObject
471 | if (!OcctGlTools::InitializeGlFbo(myView))
472 | {
473 | Q_EMIT glCriticalError("Default FBO wrapper creation failed");
474 | return;
475 | }
476 |
477 | Graphic3d_Vec2i aViewSizeNew; myView->Window()->Size(aViewSizeNew.x(), aViewSizeNew.y());
478 | if (aViewSizeNew != aViewSizeOld || myView->Window()->DevicePixelRatio() != aDevPixelRatioOld)
479 | dumpGlInfo(true, false);
480 |
481 | // reset global GL state from Qt before redrawing OCCT
482 | OcctGlTools::ResetGlStateBeforeOcct(myView);
483 |
484 | // flush pending input events and redraw the viewer
485 | myView->InvalidateImmediate();
486 | AIS_ViewController::FlushViewEvents(myContext, myView, true);
487 |
488 | // reset global GL state after OCCT before redrawing Qt
489 | // (alternative to QQuickOpenGLUtils::resetOpenGLState())
490 | OcctGlTools::ResetGlStateAfterOcct(myView);
491 | /*#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
492 | QQuickOpenGLUtils::resetOpenGLState()
493 | #else
494 | if (aQWindow != nullptr)
495 | aQWindow->resetOpenGLState();
496 | #endif*/
497 | }
498 |
--------------------------------------------------------------------------------
/occt-qt-tools/OcctQtTools.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025 Kirill Gavrilov
2 |
3 | #include "OcctQtTools.h"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | // ================================================================
18 | // Function : qtColorToOcct
19 | // ================================================================
20 | Quantity_Color OcctQtTools::qtColorToOcct(const QColor& theColor)
21 | {
22 | return Quantity_Color(theColor.red() / 255.0, theColor.green() / 255.0, theColor.blue() / 255.0, Quantity_TOC_sRGB);
23 | }
24 |
25 | // ================================================================
26 | // Function : qtColorFromOcct
27 | // ================================================================
28 | QColor OcctQtTools::qtColorFromOcct(const Quantity_Color& theColor)
29 | {
30 | NCollection_Vec3 anRgb; theColor.Values(anRgb.r(), anRgb.g(), anRgb.b(), Quantity_TOC_sRGB);
31 | return QColor((int)Round(anRgb.r() * 255.0), (int)Round(anRgb.g() * 255.0), (int)Round(anRgb.b() * 255.0));
32 | }
33 |
34 | // ================================================================
35 | // Function : qtColorToOcctRgba
36 | // ================================================================
37 | Quantity_ColorRGBA OcctQtTools::qtColorToOcctRgba(const QColor& theColor)
38 | {
39 | return Quantity_ColorRGBA(OcctQtTools::qtColorToOcct(theColor), theColor.alpha() / 255.0f);
40 | }
41 |
42 | // ================================================================
43 | // Function : qtColorFromOcctRgba
44 | // ================================================================
45 | QColor OcctQtTools::qtColorFromOcctRgba(const Quantity_ColorRGBA& theColor)
46 | {
47 | QColor aColor = OcctQtTools::qtColorFromOcct(theColor.GetRGB());
48 | aColor.setAlpha((int)Round(theColor.Alpha() * 255.0));
49 | return aColor;
50 | }
51 |
52 | // ================================================================
53 | // Function : qtStringToOcct
54 | // ================================================================
55 | TCollection_AsciiString OcctQtTools::qtStringToOcct(const QString& theText)
56 | {
57 | return TCollection_AsciiString(theText.toUtf8().data());
58 | }
59 |
60 | // ================================================================
61 | // Function : qtStringFromOcct
62 | // ================================================================
63 | QString OcctQtTools::qtStringFromOcct(const TCollection_AsciiString& theText)
64 | {
65 | return QString::fromUtf8(theText.ToCString());
66 | }
67 |
68 | // ================================================================
69 | // Function : qtStringToOcctExt
70 | // ================================================================
71 | TCollection_ExtendedString OcctQtTools::qtStringToOcctExt(const QString& theText)
72 | {
73 | return TCollection_ExtendedString(theText.toUtf8().data(), true);
74 | }
75 |
76 | // ================================================================
77 | // Function : qtStringFromOcctExt
78 | // ================================================================
79 | QString OcctQtTools::qtStringFromOcctExt(const TCollection_ExtendedString& theText)
80 | {
81 | return QString::fromUtf16(theText.ToExtString());
82 | }
83 |
84 | // ================================================================
85 | // Function : qtMsgTypeToGravity
86 | // ================================================================
87 | Message_Gravity OcctQtTools::qtMsgTypeToGravity(QtMsgType theType)
88 | {
89 | switch (theType)
90 | {
91 | case QtDebugMsg: return Message_Trace;
92 | case QtInfoMsg: return Message_Info;
93 | case QtWarningMsg: return Message_Warning;
94 | case QtCriticalMsg: return Message_Alarm;
95 | case QtFatalMsg: return Message_Fail;
96 | default: return Message_Fail;
97 | }
98 | }
99 |
100 | // ================================================================
101 | // Function : qtMessageHandlerToOcct
102 | // ================================================================
103 | void OcctQtTools::qtMessageHandlerToOcct(QtMsgType theType,
104 | const QMessageLogContext& theCtx,
105 | const QString& theMsg)
106 | {
107 | Message_Gravity aGravity = OcctQtTools::qtMsgTypeToGravity(theType);
108 | if (aGravity < (int)Message_Alarm)
109 | aGravity = Message_Trace; // lower gravity to trace for non-critical errors
110 |
111 | const QString aQMsg = qFormatLogMessage(theType, theCtx, theMsg);
112 | const TCollection_AsciiString aMsg = aQMsg.toUtf8().data();
113 | Message::Send(aMsg, aGravity);
114 | }
115 |
116 | // ================================================================
117 | // Function : qtGlPlatformSetup
118 | // ================================================================
119 | void OcctQtTools::qtGlPlatformSetup()
120 | {
121 | #if defined(_WIN32)
122 | // never use ANGLE on Windows, since OCCT 3D Viewer does not expect this;
123 | // use Qt::AA_UseOpenGLES for embedded systems
124 | QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
125 | #elif defined(__APPLE__)
126 | //
127 | #else
128 | // Qt6 tries to use Wayland platform by default, which is incompatible with OCCT depending on Xlib;
129 | // Force 'xcb' platform plugin (alternatively, could be passed QApplication as '-platform xcb' argument).
130 | OSD_Environment aQpaPlat("QT_QPA_PLATFORM");
131 | if (aQpaPlat.Value().IsEmpty())
132 | {
133 | aQpaPlat.SetValue("xcb");
134 | aQpaPlat.Build();
135 | }
136 | #endif
137 |
138 | // request Qt to share resources between OpenGL contexts like QOpenGLWidget and QQuickWidget;
139 | // allows detaching OCCT 3D Viewer widget inside QDockWidget into new top-level window
140 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
141 |
142 | /*#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
143 | // workaround for some bugs in Qt5
144 | OSD_Environment aQGlyph("QT_ENABLE_GLYPH_CACHE_WORKAROUND");
145 | if (aQGlyph.Value().IsEmpty())
146 | {
147 | aQGlyph.SetValue("1");
148 | aQGlyph.Build();
149 | }
150 | #endif*/
151 |
152 | // global OpenGL setup managed by Qt
153 | const QSurfaceFormat aGlFormat = OcctQtTools::qtGlSurfaceFormat();
154 | QSurfaceFormat::setDefaultFormat(aGlFormat);
155 |
156 | // ask Qt managing rendering from GUI thread instead of QSGRenderThread
157 | // for QtQuick applications
158 | /*OSD_Environment aQsgLoop("QSG_RENDER_LOOP");
159 | if (aQsgLoop.Value().IsEmpty())
160 | {
161 | aQsgLoop.SetValue("basic");
162 | aQsgLoop.Build();
163 | }*/
164 |
165 | // enable auto-scaling for high-density screens and fractional scale factors
166 | // (this is default since Qt6)
167 | #if (QT_VERSION_MAJOR == 5)
168 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
169 | #if (QT_VERSION_MINOR >= 14)
170 | QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
171 | #endif
172 | #endif
173 |
174 | // redirect Qt messages to OCCT
175 | /*static QtMessageHandler aDefHandler = qInstallMessageHandler([](QtMsgType theType,
176 | const QMessageLogContext& theCtx,
177 | const QString& theMsg){
178 | OcctQtTools::qtMessageHandlerToOcct(theType, theCtx, theMsg);
179 | //if (aDefHandler != nullptr) { aDefHandler(QtCriticalMsg, theCtx, theMsg); }
180 | });*/
181 | }
182 |
183 | // ================================================================
184 | // Function : qtGlSurfaceFormat
185 | // ================================================================
186 | QSurfaceFormat OcctQtTools::qtGlSurfaceFormat(QSurfaceFormat::OpenGLContextProfile theProfile,
187 | bool theToDebug)
188 | {
189 | const bool isDeepColor = false;
190 | QSurfaceFormat::OpenGLContextProfile aProfile = theProfile;
191 | if (theProfile == QSurfaceFormat::NoProfile)
192 | {
193 | aProfile = QSurfaceFormat::CompatibilityProfile;
194 | #ifdef __APPLE__
195 | // suppress Qt warning "QCocoaGLContext: Falling back to unshared context"
196 | aProfile = QSurfaceFormat::CoreProfile;
197 | #endif
198 | }
199 |
200 | QSurfaceFormat aGlFormat;
201 | if (isDeepColor)
202 | {
203 | aGlFormat.setRedBufferSize(10);
204 | aGlFormat.setGreenBufferSize(10);
205 | aGlFormat.setBlueBufferSize(10);
206 | aGlFormat.setAlphaBufferSize(2);
207 | }
208 | aGlFormat.setDepthBufferSize(24);
209 | aGlFormat.setStencilBufferSize(8);
210 | aGlFormat.setProfile(aProfile);
211 | if (aProfile == QSurfaceFormat::CoreProfile)
212 | aGlFormat.setVersion(4, 5);
213 |
214 | // request sRGBColorSpace color-space to meet OCCT expectations or use OcctQtFrameBuffer fallback.
215 | /*#if (QT_VERSION_MAJOR > 5) || (QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR >= 10)
216 | aGlFormat.setColorSpace(QSurfaceFormat::sRGBColorSpace);
217 | #endif*/
218 |
219 | if (theToDebug)
220 | aGlFormat.setOption(QSurfaceFormat::DebugContext, true);
221 |
222 | return aGlFormat;
223 | }
224 |
225 | // ================================================================
226 | // Function : qtGlCapsFromSurfaceFormat
227 | // ================================================================
228 | void OcctQtTools::qtGlCapsFromSurfaceFormat(OpenGl_Caps& theCaps, const QSurfaceFormat& theFormat)
229 | {
230 | theCaps.contextDebug = theFormat.testOption(QSurfaceFormat::DebugContext);
231 | theCaps.contextSyncDebug = theCaps.contextDebug;
232 | theCaps.contextCompatible = theFormat.profile() != QSurfaceFormat::CoreProfile;
233 | #if (OCC_VERSION_HEX >= 0x070700)
234 | theCaps.buffersDeepColor = theFormat.redBufferSize() == 10
235 | && theFormat.greenBufferSize() == 10
236 | && theFormat.blueBufferSize() == 10;
237 | #endif
238 | }
239 |
240 | // ================================================================
241 | // Function : qtHandleHoverEvent
242 | // ================================================================
243 | bool OcctQtTools::qtHandleHoverEvent(Aspect_WindowInputListener& theListener,
244 | const Handle(V3d_View)& theView,
245 | const QHoverEvent* theEvent)
246 | {
247 | if (theView->Window().IsNull())
248 | return false;
249 |
250 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
251 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y());
252 | #else
253 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y());
254 | #endif
255 | const Graphic3d_Vec2i aPnt2i(theView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5));
256 | const Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
257 | const Aspect_VKeyFlags aFlags = OcctQtTools::qtMouseModifiers2VKeys(theEvent->modifiers());
258 | return theListener.UpdateMousePosition(aPnt2i, aButtons, aFlags, false);
259 | }
260 |
261 | // ================================================================
262 | // Function : qtHandleMouseEvent
263 | // ================================================================
264 | bool OcctQtTools::qtHandleMouseEvent(Aspect_WindowInputListener& theListener,
265 | const Handle(V3d_View)& theView,
266 | const QMouseEvent* theEvent)
267 | {
268 | if (theView->Window().IsNull())
269 | return false;
270 |
271 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
272 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y());
273 | #else
274 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y());
275 | #endif
276 | const Graphic3d_Vec2i aPnt2i(theView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5));
277 | const Aspect_VKeyMouse aButtons = OcctQtTools::qtMouseButtons2VKeys(theEvent->buttons());
278 | const Aspect_VKeyFlags aFlags = OcctQtTools::qtMouseModifiers2VKeys(theEvent->modifiers());
279 | if (theEvent->type() == QEvent::MouseMove)
280 | return theListener.UpdateMousePosition(aPnt2i, aButtons, aFlags, false);
281 |
282 | return theListener.UpdateMouseButtons(aPnt2i, aButtons, aFlags, false);
283 | }
284 |
285 | // ================================================================
286 | // Function : qtHandleWheelEvent
287 | // ================================================================
288 | bool OcctQtTools::qtHandleWheelEvent(Aspect_WindowInputListener& theListener,
289 | const Handle(V3d_View)& theView,
290 | const QWheelEvent* theEvent)
291 | {
292 | if (theView->Window().IsNull())
293 | return false;
294 |
295 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
296 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y());
297 | #else
298 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y());
299 | #endif
300 | const Graphic3d_Vec2i aPnt2i(theView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5));
301 | return theListener.UpdateMouseScroll(Aspect_ScrollDelta(aPnt2i, double(theEvent->angleDelta().y()) / 120.0));
302 | }
303 |
304 | // ================================================================
305 | // Function : qtHandleTouchEvent
306 | // ================================================================
307 | bool OcctQtTools::qtHandleTouchEvent(Aspect_WindowInputListener& theListener,
308 | const Handle(V3d_View)& theView,
309 | const QTouchEvent* theEvent)
310 | {
311 | if (theView->Window().IsNull())
312 | return false;
313 |
314 | bool hasUpdates = false;
315 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
316 | for (const QTouchEvent::TouchPoint& aQTouch : theEvent->points())
317 | {
318 | const Standard_Size aTouchId = aQTouch.id();
319 | const Graphic3d_Vec2d aNewPos2d =
320 | theView->Window()->ConvertPointToBacking(Graphic3d_Vec2d(aQTouch.position().x(), aQTouch.position().y()));
321 | const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i(aNewPos2d + Graphic3d_Vec2d(0.5));
322 | if (aQTouch.state() == QEventPoint::Pressed
323 | && aNewPos2i.minComp() >= 0)
324 | {
325 | hasUpdates = true;
326 | theListener.AddTouchPoint(aTouchId, aNewPos2d);
327 | }
328 | else if (aQTouch.state() == QEventPoint::Updated
329 | && theListener.TouchPoints().Contains(aTouchId))
330 | {
331 | hasUpdates = true;
332 | theListener.UpdateTouchPoint(aTouchId, aNewPos2d);
333 | }
334 | else if (aQTouch.state() == QEventPoint::Released
335 | && theListener.RemoveTouchPoint(aTouchId))
336 | {
337 | hasUpdates = true;
338 | }
339 | }
340 | #else
341 | for (const QTouchEvent::TouchPoint& aQTouch : theEvent->touchPoints())
342 | {
343 | const Standard_Size aTouchId = aQTouch.id();
344 | const Graphic3d_Vec2d aNewPos2d =
345 | theView->Window()->ConvertPointToBacking(Graphic3d_Vec2d(aQTouch.pos().x(), aQTouch.pos().y()));
346 | const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i(aNewPos2d + Graphic3d_Vec2d(0.5));
347 | if (aQTouch.state() == Qt::TouchPointPressed
348 | && aNewPos2i.minComp() >= 0)
349 | {
350 | hasUpdates = true;
351 | theListener.AddTouchPoint(aTouchId, aNewPos2d);
352 | }
353 | else if (aQTouch.state() == Qt::TouchPointMoved
354 | && theListener.TouchPoints().Contains(aTouchId))
355 | {
356 | hasUpdates = true;
357 | theListener.UpdateTouchPoint(aTouchId, aNewPos2d);
358 | }
359 | else if (aQTouch.state() == Qt::TouchPointReleased
360 | && theListener.RemoveTouchPoint(aTouchId))
361 | {
362 | hasUpdates = true;
363 | }
364 | }
365 | #endif
366 | return hasUpdates;
367 | }
368 |
369 | // ================================================================
370 | // Function : qtMouseButtons2VKeys
371 | // ================================================================
372 | Aspect_VKeyMouse OcctQtTools::qtMouseButtons2VKeys(Qt::MouseButtons theButtons)
373 | {
374 | Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
375 | if ((theButtons & Qt::LeftButton) != 0)
376 | aButtons |= Aspect_VKeyMouse_LeftButton;
377 |
378 | if ((theButtons & Qt::MiddleButton) != 0)
379 | aButtons |= Aspect_VKeyMouse_MiddleButton;
380 |
381 | if ((theButtons & Qt::RightButton) != 0)
382 | aButtons |= Aspect_VKeyMouse_RightButton;
383 |
384 | return aButtons;
385 | }
386 |
387 | // ================================================================
388 | // Function : qtMouseModifiers2VKeys
389 | // ================================================================
390 | Aspect_VKeyFlags OcctQtTools::qtMouseModifiers2VKeys(Qt::KeyboardModifiers theModifiers)
391 | {
392 | Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
393 | if ((theModifiers & Qt::ShiftModifier) != 0)
394 | aFlags |= Aspect_VKeyFlags_SHIFT;
395 |
396 | if ((theModifiers & Qt::ControlModifier) != 0)
397 | aFlags |= Aspect_VKeyFlags_CTRL;
398 |
399 | if ((theModifiers & Qt::AltModifier) != 0)
400 | aFlags |= Aspect_VKeyFlags_ALT;
401 |
402 | return aFlags;
403 | }
404 |
405 | // ================================================================
406 | // Function : qtKey2VKey
407 | // ================================================================
408 | Aspect_VKey OcctQtTools::qtKey2VKey(int theKey)
409 | {
410 | switch (theKey)
411 | {
412 | case 1060: // ru
413 | case Qt::Key_A:
414 | return Aspect_VKey_A;
415 | case 1048: // ru
416 | case Qt::Key_B:
417 | return Aspect_VKey_B;
418 | case 1057: // ru
419 | case Qt::Key_C:
420 | return Aspect_VKey_C;
421 | case 1042: // ru
422 | case Qt::Key_D:
423 | return Aspect_VKey_D;
424 | case 1059: // ru
425 | case Qt::Key_E:
426 | return Aspect_VKey_E;
427 | case 1040: // ru
428 | case Qt::Key_F:
429 | return Aspect_VKey_F;
430 | case Qt::Key_G:
431 | return Aspect_VKey_G;
432 | case Qt::Key_H:
433 | return Aspect_VKey_H;
434 | case Qt::Key_I:
435 | return Aspect_VKey_I;
436 | case Qt::Key_J:
437 | return Aspect_VKey_J;
438 | case Qt::Key_K:
439 | return Aspect_VKey_K;
440 | case 1044: // ru
441 | case Qt::Key_L:
442 | return Aspect_VKey_L;
443 | case Qt::Key_M:
444 | return Aspect_VKey_M;
445 | case Qt::Key_N:
446 | return Aspect_VKey_N;
447 | case Qt::Key_O:
448 | return Aspect_VKey_O;
449 | case Qt::Key_P:
450 | return Aspect_VKey_P;
451 | case 1049: // ru
452 | case Qt::Key_Q:
453 | return Aspect_VKey_Q;
454 | case 1050: // ru
455 | case Qt::Key_R:
456 | return Aspect_VKey_R;
457 | case 1067: // ru
458 | case Qt::Key_S:
459 | return Aspect_VKey_S;
460 | case 1045: // ru
461 | case Qt::Key_T:
462 | return Aspect_VKey_T;
463 | case Qt::Key_U:
464 | return Aspect_VKey_U;
465 | case 1052: // ru
466 | case Qt::Key_V:
467 | return Aspect_VKey_V;
468 | case 1062: // ru
469 | case Qt::Key_W:
470 | return Aspect_VKey_W;
471 | case 1063: // ru
472 | case Qt::Key_X:
473 | return Aspect_VKey_X;
474 | case Qt::Key_Y:
475 | return Aspect_VKey_Y;
476 | case 1071: // ru
477 | case Qt::Key_Z:
478 | return Aspect_VKey_Z;
479 | //
480 | case Qt::Key_0:
481 | return Aspect_VKey_0;
482 | case Qt::Key_1:
483 | return Aspect_VKey_1;
484 | case Qt::Key_2:
485 | return Aspect_VKey_2;
486 | case Qt::Key_3:
487 | return Aspect_VKey_3;
488 | case Qt::Key_4:
489 | return Aspect_VKey_4;
490 | case Qt::Key_5:
491 | return Aspect_VKey_5;
492 | case Qt::Key_6:
493 | return Aspect_VKey_6;
494 | case Qt::Key_7:
495 | return Aspect_VKey_7;
496 | case Qt::Key_8:
497 | return Aspect_VKey_8;
498 | case Qt::Key_9:
499 | return Aspect_VKey_9;
500 | //
501 | case Qt::Key_F1:
502 | return Aspect_VKey_F1;
503 | case Qt::Key_F2:
504 | return Aspect_VKey_F2;
505 | case Qt::Key_F3:
506 | return Aspect_VKey_F3;
507 | case Qt::Key_F4:
508 | return Aspect_VKey_F4;
509 | case Qt::Key_F5:
510 | return Aspect_VKey_F5;
511 | case Qt::Key_F6:
512 | return Aspect_VKey_F6;
513 | case Qt::Key_F7:
514 | return Aspect_VKey_F7;
515 | case Qt::Key_F8:
516 | return Aspect_VKey_F8;
517 | case Qt::Key_F9:
518 | return Aspect_VKey_F9;
519 | case Qt::Key_F10:
520 | return Aspect_VKey_F10;
521 | case Qt::Key_F11:
522 | return Aspect_VKey_F11;
523 | case Qt::Key_F12:
524 | return Aspect_VKey_F12;
525 | //
526 | case Qt::Key_Up:
527 | return Aspect_VKey_Up;
528 | case Qt::Key_Left:
529 | return Aspect_VKey_Left;
530 | case Qt::Key_Right:
531 | return Aspect_VKey_Right;
532 | case Qt::Key_Down:
533 | return Aspect_VKey_Down;
534 | case Qt::Key_Plus:
535 | return Aspect_VKey_Plus;
536 | case Qt::Key_Minus:
537 | return Aspect_VKey_Minus;
538 | case Qt::Key_Equal:
539 | return Aspect_VKey_Equal;
540 | case Qt::Key_PageDown:
541 | return Aspect_VKey_PageDown;
542 | case Qt::Key_PageUp:
543 | return Aspect_VKey_PageUp;
544 | case Qt::Key_Home:
545 | return Aspect_VKey_Home;
546 | case Qt::Key_End:
547 | return Aspect_VKey_End;
548 | case Qt::Key_Escape:
549 | return Aspect_VKey_Escape;
550 | case Qt::Key_Back:
551 | return Aspect_VKey_Back;
552 | case Qt::Key_Enter:
553 | return Aspect_VKey_Enter;
554 | case Qt::Key_Backspace:
555 | return Aspect_VKey_Backspace;
556 | case Qt::Key_Space:
557 | return Aspect_VKey_Space;
558 | case Qt::Key_Delete:
559 | return Aspect_VKey_Delete;
560 | case Qt::Key_Tab:
561 | return Aspect_VKey_Tab;
562 | case 1025:
563 | case Qt::Key_QuoteLeft:
564 | return Aspect_VKey_Tilde;
565 | //
566 | case Qt::Key_Shift:
567 | return Aspect_VKey_Shift;
568 | case Qt::Key_Control:
569 | return Aspect_VKey_Control;
570 | case Qt::Key_Alt:
571 | return Aspect_VKey_Alt;
572 | case Qt::Key_Menu:
573 | return Aspect_VKey_Menu;
574 | case Qt::Key_Meta:
575 | return Aspect_VKey_Meta;
576 | default:
577 | return Aspect_VKey_UNKNOWN;
578 | }
579 | }
580 |
--------------------------------------------------------------------------------