1 /*========================================================================= 2 3 Program: Visualization Toolkit 4 Module: QQuickVTKRenderWindow.h 5 6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 7 All rights reserved. 8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details. 9 10 This software is distributed WITHOUT ANY WARRANTY; without even 11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 PURPOSE. See the above copyright notice for more information. 13 14 =========================================================================*/ 15 /** 16 * @class QQuickVTKRenderWindow 17 * @brief [QQuickItem] subclass that manages the vtkRenderWindow and, in 18 * turn, the OpenGL context of the QML application 19 * 20 * QQuickVTKRenderWindow extends [QQuickItem] in a way that allows for VTK to get a handle to, and 21 * draw inside of the QtQuick scenegraph, using OpenGL draw calls. 22 * 23 * This item is exported to the QML layer via the QQmlVTKPlugin under the module VTK. It is 24 * registered as a type \b VTKRenderWindow. Since, this class is intended to manage an OpenGL 25 * context in the window, a single instance would be needed for most QML applications. 26 * 27 * Typical usage for QQuickVTKRenderWindow in a Qml application is as follows: 28 * 29 * @code 30 * // import related modules 31 * import QtQuick 2.15 32 * import QtQuick.Controls 2.15 33 * import QtQuick.Window 2.15 34 * 35 * // import the VTK module 36 * import VTK 9.0 37 * 38 * // window containing the application 39 * ApplicationWindow { 40 * // title of the application 41 * title: qsTr("VTK QtQuick App") 42 * width: 400 43 * height: 400 44 * color: palette.window 45 * 46 * SystemPalette { 47 * id: palette 48 * colorGroup: SystemPalette.Active 49 * } 50 * 51 * // Instantiate the vtk render window 52 * VTKRenderWindow { 53 * id: vtkwindow 54 * width: 400 55 * height: 400 56 * } 57 * 58 * // add one or more vtk render items 59 * VTKRenderItem { 60 * objectName: "ConeView" 61 * x: 200 62 * y: 200 63 * width: 200 64 * height: 200 65 * // Provide the handle to the render window 66 * renderWindow: vtkwindow 67 * } 68 * VTKRenderItem { 69 * objectName: "VolumeView" 70 * x: 0 71 * y: 0 72 * width: 200 73 * height: 200 74 * // Provide the handle to the render window 75 * renderWindow: vtkwindow 76 * } 77 * } 78 * @endcode 79 * 80 * To ensure that the graphics backend set up by QtQuick matches that expected by VTK, use the 81 * method QQuickVTKRenderWindow::setupGraphicsBackend() before a QApplication/QGuiApplication is 82 * instantiated in the main method of the application. 83 * 84 * @code 85 * int main(int argc, char* argv[]) 86 * { 87 * // Setup the graphics backend 88 * QQuickVTKRenderWindow::setupGraphicsBackend(); 89 * QGuiApplication app(argc, argv); 90 * ... 91 * return EXIT_SUCCESS; 92 * } 93 * @endcode 94 * 95 * The VTK pipeline can be then set up for each \b VTKRenderItem in the C++ code. 96 * 97 * ## QtQuick scenegraph and threaded render loop 98 * 99 * QtQuick/QML scenegraph rendering is done via private API inside the [QQuickWindow] class. For 100 * details on QtQuick's render loop, see [QtQuick Scenegraph Rendering]( 101 * https://doc.qt.io/qt-6/qtquick-visualcanvas-scenegraph.html#scene-graph-and-rendering). 102 * Qt automatically decides between a threaded and basic render loop for most applications. 103 * QQuickVTKRenderWindow and QQuickVTKRenderItem support both these variants of the QtQuick render 104 * loop. 105 * 106 * When the scenegraph render loop is threaded, i.e. there is a dedicated rendering thread, vtk 107 * sticks to doing all rendering on this render thread. This means that all the vtk classes, 108 * pipelines etc. can be set up on the main thread but vtkRenderWindow::Render should only be 109 * invoked on the render thread. Care must be taken not to call Render on the main thread because 110 * the OpenGL context would not be valid on the main thread. 111 * 112 * [QQuickItem]: https://doc.qt.io/qt-5/qquickitem.html 113 * [QQuickWindow]: https://doc.qt.io/qt-5/qquickwindow.html 114 */ 115 116 #ifndef QQuickVTKRenderWindow_h 117 #define QQuickVTKRenderWindow_h 118 119 // vtk includes 120 #include "vtkSmartPointer.h" // For vtkSmartPointer 121 122 // Qt includes 123 #include <QOpenGLFunctions> // For QOpenGLFunctions 124 #include <QPointer> // For QPointer 125 #include <QQuickItem> 126 127 #include "vtkGUISupportQtQuickModule.h" // for export macro 128 129 // Forward declarations 130 class QEvent; 131 class QQuickVTKInteractorAdapter; 132 class QQuickWindow; 133 class QWheelEvent; 134 class vtkGenericOpenGLRenderWindow; 135 class vtkImageData; 136 class vtkRenderWindow; 137 class vtkRenderer; 138 class vtkWindowToImageFilter; 139 140 class VTKGUISUPPORTQTQUICK_EXPORT QQuickVTKRenderWindow 141 : public QQuickItem 142 , protected QOpenGLFunctions 143 { 144 Q_OBJECT 145 typedef QQuickItem Superclass; 146 147 public: 148 /** 149 * Constructor 150 * Creates a QQuickVTKRenderWindow with: 151 * - a vtkGenericOpenGLRenderWindow to manage the OpenGL context 152 * - an interactor adapter to forward Qt events to vtk's interactor 153 */ 154 QQuickVTKRenderWindow(QQuickItem* parent = nullptr); 155 156 /** 157 * Destructor 158 */ 159 ~QQuickVTKRenderWindow(); 160 161 /** 162 * Set up the graphics surface format and api. 163 * 164 * This method sets the graphics API to OpenGLRhi and sets up the surface format for intermixed 165 * VTK and QtQuick rendering. 166 * Use this method before instantiating a QApplication/QGuiApplication in a QtQuick/QML app with 167 * a VTK render view like QQuickVTKRenderItem. 168 */ 169 static void setupGraphicsBackend(); 170 171 ///@{ 172 /** 173 * Set/Get the vtkRenderWindow for the view. 174 * Note that this render window should be of type vtkGenericOpenGLRenderWindow. This is necessary 175 * since that would allow vtk's opengl draw calls to work seamlessly inside the QtQuick created 176 * scenegraph and OpenGL context. 177 * 178 * By default, a vtkGenericOpenGLRenderWindow is created and set on this item at construction 179 * time. 180 */ 181 void setRenderWindow(vtkRenderWindow* renWin); 182 void setRenderWindow(vtkGenericOpenGLRenderWindow* renWin); 183 vtkRenderWindow* renderWindow() const; 184 ///@} 185 186 /** 187 * Map a Qt item rect to viewport coordinates 188 */ 189 virtual void mapToViewport(const QRectF& rect, double viewport[4]); 190 191 /** 192 * Get access to the interactor adapter 193 */ 194 QPointer<QQuickVTKInteractorAdapter> interactorAdapter() const; 195 196 ///@{ 197 /** 198 * Capture a screenshot of the window 199 * 200 * \param viewport area to capture. 201 * \returns Image data containing the window capture. 202 * \note This triggers a scenegraph update to capture the render window view. 203 */ 204 virtual vtkSmartPointer<vtkImageData> captureScreenshot(); 205 virtual vtkSmartPointer<vtkImageData> captureScreenshot(double* viewport); 206 ///@} 207 208 /** 209 * Get whether the render window is initialized 210 * Used internally to determine if the OpenGL context, QQuickWindow, children items and viewports 211 * have been initialized. 212 */ 213 virtual bool isInitialized() const; 214 215 public Q_SLOTS: 216 /** 217 * This is the function called on the QtQuick render thread before the scenegraph state 218 * is synchronized. This is where most of the pipeline updates, camera manipulations, etc. and 219 * other pre-render steps can be performed. 220 * 221 * \note At the time of this method execution, the GUI thread is blocked. Hence, it is safe to 222 * perform state synchronization between the GUI elements and the VTK classes here. 223 */ 224 virtual void sync(); 225 226 /** 227 * Initialize the VTK render window for OpenGL based on the context created by QtQuick 228 * 229 * \note This method is called at the beforeRenderPassRecording stage of the QtQuick scenegraph. 230 * All the QtQuick element rendering is stacked visually above the vtk rendering. 231 */ 232 virtual void init(); 233 234 /** 235 * This is the function called on the QtQuick render thread right before the scenegraph is 236 * rendered. This is the stage where all the vtk rendering is performed. Applications would rarely 237 * need to override this method. 238 * 239 * \note This method is called at the beforeRenderPassRecording stage of the QtQuick scenegraph. 240 * All the QtQuick element rendering is stacked visually above the vtk rendering. 241 */ 242 virtual void paint(); 243 244 /** 245 * This is the function called on the QtQuick render thread when the scenegraph is invalidated. 246 * This is where all graphics resources allocated by vtk are released. 247 */ 248 virtual void cleanup(); 249 250 /** 251 * Convenience method that schedules a scenegraph update and waits for the update. 252 * \sa render() 253 */ 254 virtual void renderNow(); 255 256 /** 257 * Schedule a scenegraph update 258 * 259 * \note Since this schedules a scenegraph update, it does not guarantee that the scene will be 260 * updated after this call. 261 * \sa renderNow() 262 */ 263 virtual void render(); 264 265 protected Q_SLOTS: 266 virtual void handleWindowChanged(QQuickWindow* w); 267 268 protected: 269 QPointer<QQuickVTKInteractorAdapter> m_interactorAdapter; 270 vtkSmartPointer<vtkGenericOpenGLRenderWindow> m_renderWindow; 271 bool m_initialized = false; 272 273 // Screenshot stuff 274 bool m_screenshotScheduled = false; 275 vtkNew<vtkWindowToImageFilter> m_screenshotFilter; 276 vtkNew<vtkRenderer> m_dummyRenderer; 277 278 // Event handlers 279 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 280 void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) override; 281 #else 282 void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; 283 #endif 284 285 /** 286 * Check the scenegraph backend and graphics API being used. 287 */ 288 bool checkGraphicsBackend(); 289 290 private: 291 QQuickVTKRenderWindow(const QQuickVTKRenderWindow&) = delete; 292 void operator=(const QQuickVTKRenderWindow) = delete; 293 }; 294 295 #endif // QQuickVTKRenderWindow_h 296