diff --git a/Modules/OpenViewCore/files.cmake b/Modules/OpenViewCore/files.cmake index 41db844fff..f73869115a 100644 --- a/Modules/OpenViewCore/files.cmake +++ b/Modules/OpenViewCore/files.cmake @@ -1,21 +1,21 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES vtkQtConnection.cxx QVTKMitkInteractorAdapter.cxx QVTKQuickItem.cxx QVTKFramebufferObjectRenderer.cxx - QVTKInternalOpenglRenderWindow.cxx + vtkInternalOpenglRenderWindow.cxx ) set(MOC_H_FILES include/QVTKMitkInteractorAdapter.h include/QVTKQuickItem.h include/vtkQtConnection.h ) set(UI_FILES ) set(QRC_FILES ) diff --git a/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h b/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h index c0c0271bad..3fba37532b 100644 --- a/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h +++ b/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h @@ -1,50 +1,50 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __QVTKFramebufferObjectRenderer_h #define __QVTKFramebufferObjectRenderer_h #include -#include -#include "QVTKInternalOpenglRenderWindow.h" #include "QVTKQuickItem.h" #include +class vtkInternalOpenGLRenderWindow; + +//! Part of Qml rendering prototype, see QmlMitkRenderWindowItem. class MITKOPENVIEWCORE_EXPORT QVTKFramebufferObjectRenderer : public QQuickFramebufferObject::Renderer { public: bool m_neverRendered; bool m_readyToRender; vtkInternalOpenGLRenderWindow *m_vtkRenderWindow; - vtkFrameBufferObject2* m_vtkFBO; QVTKQuickItem *m_vtkQuickItem; public: QVTKFramebufferObjectRenderer(vtkInternalOpenGLRenderWindow *rw); ~QVTKFramebufferObjectRenderer(); virtual void synchronize(QQuickFramebufferObject * item); virtual void render(); QOpenGLFramebufferObject *createFramebufferObject(const QSize &size); friend class vtkInternalOpenGLRenderWindow; }; #endif diff --git a/Modules/OpenViewCore/include/QVTKMitkInteractorAdapter.h b/Modules/OpenViewCore/include/QVTKMitkInteractorAdapter.h index 2719bb5c91..b38231a1cd 100644 --- a/Modules/OpenViewCore/include/QVTKMitkInteractorAdapter.h +++ b/Modules/OpenViewCore/include/QVTKMitkInteractorAdapter.h @@ -1,86 +1,87 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // original copyright below /*========================================================================= Program: Visualization Toolkit Module: QVTKMitkInteractorAdapter.h Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /*========================================================================= Copyright 2004 Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive license for use of this work by or on behalf of the U.S. Government. Redistribution and use in source and binary forms, with or without modification, are permitted provided that this Notice and any statement of authorship are reproduced on all copies. =========================================================================*/ /*======================================================================== For general information about using VTK and Qt, see: http://www.trolltech.com/products/3rdparty/vtksupport.html =========================================================================*/ // .NAME QVTKMitkInteractorAdapter - Handle Qt events. // .SECTION Description // QVTKInteractor handles relaying Qt events to VTK. #ifndef Q_VTK_MITK_INTERACTOR_ADAPTER_H #define Q_VTK_MITK_INTERACTOR_ADAPTER_H #include #include class vtkRenderWindowInteractor; class QEvent; // .NAME QVTKMitkInteractorAdapter - A QEvent translator. // .SECTION Description // QVTKMitkInteractorAdapter translates QEvents and send them to a // vtkRenderWindowInteractor. +//! Part of Qml rendering prototype, see QmlMitkRenderWindowItem. class MITKOPENVIEWCORE_EXPORT QVTKMitkInteractorAdapter : public QObject { Q_OBJECT public: // Description: // Constructor: takes QObject parent QVTKMitkInteractorAdapter(QObject* parent); // Description: // Destructor ~QVTKMitkInteractorAdapter(); // Description: // Process a QEvent and send it to the interactor // returns whether the event was recognized and processed bool ProcessEvent(QEvent* e, vtkRenderWindowInteractor* iren); }; #endif diff --git a/Modules/OpenViewCore/include/QVTKQuickItem.h b/Modules/OpenViewCore/include/QVTKQuickItem.h index 8d987b0099..bf6c22bc00 100644 --- a/Modules/OpenViewCore/include/QVTKQuickItem.h +++ b/Modules/OpenViewCore/include/QVTKQuickItem.h @@ -1,116 +1,122 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // original copyright below /*======================================================================== OpenView -- http://openview.kitware.com Copyright 2012 Kitware, Inc. Licensed under the BSD license. See LICENSE file for details. ========================================================================*/ #ifndef __QVTKQuickItem_h #define __QVTKQuickItem_h #include #include #include "vtkSmartPointer.h" #include "vtkNew.h" #include #include class QOpenGLContext; class QOpenGLFramebufferObject; class QVTKMitkInteractorAdapter; class QVTKInteractor; class QVTKFramebufferObjectRenderer; class vtkEventQtSlotConnect; class vtkOpenGLRenderWindow; class vtkObject; class vtkContextView; class MITKOPENVIEWCORE_EXPORT QVTKQuickItem : public QQuickFramebufferObject { Q_OBJECT public: QVTKQuickItem(QQuickItem* parent = 0); // Description: // destructor ~QVTKQuickItem(); - Renderer *createRenderer() const; + Renderer* createRenderer() const; // Description: // get the render window used with this item vtkOpenGLRenderWindow* GetRenderWindow() const; // Description: // get the render window interactor used with this item // this item enforces its own interactor QVTKInteractor* GetInteractor() const; QMutex m_viewLock; protected slots: // slot called when vtk wants to know if the context is current virtual void IsCurrent(vtkObject* caller, unsigned long vtk_event, void* client_data, void* call_data); // slot called when vtk wants to know if a window is direct virtual void IsDirect(vtkObject* caller, unsigned long vtk_event, void* client_data, void* call_data); // slot called when vtk wants to know if a window supports OpenGL virtual void SupportsOpenGL(vtkObject* caller, unsigned long vtk_event, void* client_data, void* call_data); void onTextureFollowsItemSizeChanged(bool follows); protected: // Called ONCE from the render thread before the FBO is first created and while the GUI thread is blocked virtual void init(); // Called from the render thread BEFORE each update while the GUI thread blocked virtual bool prepareForRender(); // Called from the render thread AFTER each update while the GUI thread is NOT blocked virtual void cleanupAfterRender(); // handle item key events virtual void keyPressEvent(QKeyEvent* e); virtual void keyReleaseEvent(QKeyEvent* e); // handle item mouse events virtual void mousePressEvent(QMouseEvent* e); virtual void mouseReleaseEvent(QMouseEvent* e); virtual void mouseDoubleClickEvent(QMouseEvent* e); virtual void mouseMoveEvent(QMouseEvent* e); virtual void geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry); virtual void wheelEvent(QWheelEvent* e); virtual void hoverEnterEvent(QHoverEvent* e); virtual void hoverLeaveEvent(QHoverEvent* e); virtual void hoverMoveEvent(QHoverEvent* e); QSGNode* updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *nodeData); + //! To be called from rendering thread while synchronized + //! with UI thread - should execute a list of queued UI events. + //! + //! Not yet implemented for QVTKQuickItem but possibly for sub-classes + virtual void processPendingEvents() {} + private: vtkOpenGLRenderWindow *m_win; vtkSmartPointer m_interactor; QVTKMitkInteractorAdapter* m_interactorAdapter; vtkSmartPointer m_connect; friend class QVTKFramebufferObjectRenderer; }; #endif diff --git a/Modules/OpenViewCore/src/QVTKFramebufferObjectRenderer.cxx b/Modules/OpenViewCore/src/QVTKFramebufferObjectRenderer.cxx index 6ca47209a2..5f652c0a9d 100644 --- a/Modules/OpenViewCore/src/QVTKFramebufferObjectRenderer.cxx +++ b/Modules/OpenViewCore/src/QVTKFramebufferObjectRenderer.cxx @@ -1,91 +1,98 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "QVTKInternalOpenglRenderWindow.h" +#include "vtkInternalOpenglRenderWindow.h" #include "QVTKFramebufferObjectRenderer.h" + +#include + #include #include #include #include #include #include #include #include #include #include #include #include #include #include QVTKFramebufferObjectRenderer::QVTKFramebufferObjectRenderer(vtkInternalOpenGLRenderWindow *rw) : m_vtkRenderWindow(rw), m_neverRendered(true), m_readyToRender(false) { m_vtkRenderWindow->Register(NULL); m_vtkRenderWindow->QtParentRenderer = this; } -QOpenGLFramebufferObject * QVTKFramebufferObjectRenderer::createFramebufferObject(const QSize &size) +QOpenGLFramebufferObject* QVTKFramebufferObjectRenderer::createFramebufferObject(const QSize &size) { - QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::Depth); format.setTextureTarget(GL_TEXTURE_2D); format.setInternalTextureFormat(GL_RGBA32F_ARB); - QOpenGLFramebufferObject *fbo = new QOpenGLFramebufferObject(size, format); - + QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(size, format); m_vtkRenderWindow->SetFramebufferObject(fbo); return fbo; } void QVTKFramebufferObjectRenderer::render() { if (!m_readyToRender) { return; } + // Ask VTK to render to OpenGL m_vtkQuickItem->m_viewLock.lock(); m_vtkRenderWindow->PushState(); m_vtkRenderWindow->OpenGLInitState(); m_vtkRenderWindow->InternalRender(); m_vtkRenderWindow->OpenGLEndState(); m_vtkRenderWindow->PopState(); m_vtkQuickItem->m_viewLock.unlock(); - } void QVTKFramebufferObjectRenderer::synchronize(QQuickFramebufferObject * item) { m_vtkQuickItem = static_cast(item); if (m_neverRendered) { m_neverRendered = false; m_vtkQuickItem->init(); } + + // Execute events (that might call VTK picking to obtain z coordinates) + // while UI and rendering are synchronized. + m_vtkQuickItem->processPendingEvents(); + + // Update MITK mapper list, then mapper outputs m_readyToRender = m_vtkQuickItem->prepareForRender(); } QVTKFramebufferObjectRenderer::~QVTKFramebufferObjectRenderer() { m_vtkRenderWindow->QtParentRenderer = 0; m_vtkRenderWindow->Delete(); } diff --git a/Modules/OpenViewCore/src/QVTKQuickItem.cxx b/Modules/OpenViewCore/src/QVTKQuickItem.cxx index 63d59948ec..f78974b0ef 100644 --- a/Modules/OpenViewCore/src/QVTKQuickItem.cxx +++ b/Modules/OpenViewCore/src/QVTKQuickItem.cxx @@ -1,258 +1,257 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // original copyright below /*======================================================================== OpenView -- http://openview.kitware.com Copyright 2012 Kitware, Inc. Licensed under the BSD license. See LICENSE file for details. ========================================================================*/ #include "QVTKQuickItem.h" #include #include #include #include #include #include "QVTKInteractor.h" #include "QVTKMitkInteractorAdapter.h" #include "vtkGenericOpenGLRenderWindow.h" #include "vtkEventQtSlotConnect.h" #include "vtkgl.h" #include "vtkOpenGLExtensionManager.h" #include "vtkRenderer.h" +#include "vtkRendererCollection.h" #include "vtkCubeSource.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" #include -#include "QVTKInternalOpenglRenderWindow.h" +#include "vtkInternalOpenglRenderWindow.h" #include "QVTKFramebufferObjectRenderer.h" QVTKQuickItem::QVTKQuickItem(QQuickItem* parent) : QQuickFramebufferObject(parent) { setAcceptHoverEvents(true); setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); m_interactor = vtkSmartPointer::New(); m_interactorAdapter = new QVTKMitkInteractorAdapter(this); m_connect = vtkSmartPointer::New(); m_win = vtkInternalOpenGLRenderWindow::New(); m_interactor->SetRenderWindow(m_win); m_connect->Connect(m_win, vtkCommand::WindowIsCurrentEvent, this, SLOT(IsCurrent(vtkObject*, unsigned long, void*, void*)), NULL, 0.0, Qt::DirectConnection); m_connect->Connect(m_win, vtkCommand::WindowIsDirectEvent, this, SLOT(IsDirect(vtkObject*, unsigned long, void*, void*)), NULL, 0.0, Qt::DirectConnection); m_connect->Connect(m_win, vtkCommand::WindowSupportsOpenGLEvent, this, SLOT(SupportsOpenGL(vtkObject*, unsigned long, void*, void*)), NULL, 0.0, Qt::DirectConnection); connect(this, SIGNAL(textureFollowsItemSizeChanged(bool)), this, SLOT(onTextureFollowsItemSizeChanged(bool))); } QVTKQuickItem::~QVTKQuickItem() { if(m_win) { m_connect->Disconnect(m_win, vtkCommand::WindowIsCurrentEvent, this, SLOT(IsCurrent(vtkObject*, unsigned long, void*, void*))); m_connect->Disconnect(m_win, vtkCommand::WindowIsDirectEvent, this, SLOT(IsDirect(vtkObject*, unsigned long, void*, void*))); m_connect->Disconnect(m_win, vtkCommand::WindowSupportsOpenGLEvent, this, SLOT(SupportsOpenGL(vtkObject*, unsigned long, void*, void*))); m_win->Delete(); } } QSGNode* QVTKQuickItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *nodeData) { - if (!node) { - node = QQuickFramebufferObject::updatePaintNode(node, nodeData); - QSGSimpleTextureNode *n = static_cast(node); - if (n) - n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically); - return node; + node = QQuickFramebufferObject::updatePaintNode(node, nodeData); + if ( node != nullptr ) { + QSGSimpleTextureNode* texNode = static_cast(node); + texNode->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically); } - return QQuickFramebufferObject::updatePaintNode(node, nodeData); + return node; } -QQuickFramebufferObject::Renderer *QVTKQuickItem::createRenderer() const +QQuickFramebufferObject::Renderer* QVTKQuickItem::createRenderer() const { return new QVTKFramebufferObjectRenderer(static_cast(m_win)); } vtkOpenGLRenderWindow* QVTKQuickItem::GetRenderWindow() const { return m_win; } QVTKInteractor* QVTKQuickItem::GetInteractor() const { return m_interactor; } void QVTKQuickItem::IsCurrent(vtkObject*, unsigned long, void*, void* call_data) { bool* ptr = reinterpret_cast(call_data); *ptr = QOpenGLContext::currentContext() == this->window()->openglContext(); } void QVTKQuickItem::IsDirect(vtkObject*, unsigned long, void*, void* call_data) { int* ptr = reinterpret_cast(call_data); *ptr = 1; } void QVTKQuickItem::SupportsOpenGL(vtkObject*, unsigned long, void*, void* call_data) { int* ptr = reinterpret_cast(call_data); *ptr = 1; } void QVTKQuickItem::onTextureFollowsItemSizeChanged(bool follows) { if (!follows) { qWarning("QVTKQuickItem: Mouse interaction is not (yet) supported when textureFollowsItemSize==false"); } } void QVTKQuickItem::geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry) { QQuickFramebufferObject::geometryChanged(newGeometry, oldGeometry); QSize oldSize(oldGeometry.width(), oldGeometry.height()); QSize newSize(newGeometry.width(), newGeometry.height()); QResizeEvent e(newSize, oldSize); if (m_interactorAdapter) { this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(&e, m_interactor); this->m_viewLock.unlock(); } } void QVTKQuickItem::keyPressEvent(QKeyEvent* e) { e->accept(); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(e, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::keyReleaseEvent(QKeyEvent* e) { e->accept(); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(e, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::mousePressEvent(QMouseEvent* e) { e->accept(); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(e, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::mouseReleaseEvent(QMouseEvent* e) { e->accept(); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(e, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::mouseDoubleClickEvent(QMouseEvent* e) { e->accept(); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(e, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::mouseMoveEvent(QMouseEvent* e) { e->accept(); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(e, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::wheelEvent(QWheelEvent* e) { e->accept(); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(e, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::hoverEnterEvent(QHoverEvent* e) { e->accept(); QEvent e2(QEvent::Enter); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(&e2, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::hoverLeaveEvent(QHoverEvent* e) { e->accept(); QEvent e2(QEvent::Leave); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(&e2, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::hoverMoveEvent(QHoverEvent* e) { e->accept(); QMouseEvent e2(QEvent::MouseMove, e->pos(), Qt::NoButton, Qt::NoButton, e->modifiers()); this->m_viewLock.lock(); m_interactorAdapter->ProcessEvent(&e2, m_interactor); this->m_viewLock.unlock(); update(); } void QVTKQuickItem::init() { m_win->OpenGLInitContext(); m_win->GetExtensionManager()->LoadExtension("GL_VERSION_1_4"); m_win->GetExtensionManager()->LoadExtension("GL_VERSION_2_0"); } bool QVTKQuickItem::prepareForRender() { return true; } void QVTKQuickItem::cleanupAfterRender() { } diff --git a/Modules/OpenViewCore/src/QVTKInternalOpenglRenderWindow.cxx b/Modules/OpenViewCore/src/vtkInternalOpenGLRenderWindow.cxx similarity index 96% rename from Modules/OpenViewCore/src/QVTKInternalOpenglRenderWindow.cxx rename to Modules/OpenViewCore/src/vtkInternalOpenGLRenderWindow.cxx index 4692118e1c..37495e6bc5 100644 --- a/Modules/OpenViewCore/src/QVTKInternalOpenglRenderWindow.cxx +++ b/Modules/OpenViewCore/src/vtkInternalOpenGLRenderWindow.cxx @@ -1,81 +1,83 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "QVTKInternalOpenglRenderWindow.h" +#include "vtkInternalOpenglRenderWindow.h" + #include "QVTKFramebufferObjectRenderer.h" #include #include vtkStandardNewMacro(vtkInternalOpenGLRenderWindow); vtkInternalOpenGLRenderWindow::~vtkInternalOpenGLRenderWindow() { this->OffScreenRendering = false; } vtkInternalOpenGLRenderWindow::vtkInternalOpenGLRenderWindow() : QtParentRenderer(0) { } void vtkInternalOpenGLRenderWindow::InternalRender() { Superclass::Render(); } void vtkInternalOpenGLRenderWindow::OpenGLEndState() { glDepthMask(GL_TRUE); } void vtkInternalOpenGLRenderWindow::OpenGLInitState() { Superclass::OpenGLInitState(); vtkgl::UseProgram(0); glEnable(GL_BLEND); glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL_FASTEST); glDepthMask(GL_TRUE); } void vtkInternalOpenGLRenderWindow::Render() { if (this->QtParentRenderer) { this->QtParentRenderer->update(); } } void vtkInternalOpenGLRenderWindow::SetFramebufferObject(QOpenGLFramebufferObject *fbo) { this->SetFrontBuffer(vtkgl::COLOR_ATTACHMENT0); + // Why all those buffers, too?? this->SetFrontRightBuffer(vtkgl::COLOR_ATTACHMENT0); this->SetBackLeftBuffer(vtkgl::COLOR_ATTACHMENT0); this->SetBackRightBuffer(vtkgl::COLOR_ATTACHMENT0); QSize fboSize = fbo->size(); this->SetSize(fboSize.width(), fboSize.height()); this->NumberOfFrameBuffers = 1; this->FrameBufferObject = static_cast(fbo->handle()); this->DepthRenderBufferObject = 0; // static_cast(depthRenderBufferObject); this->TextureObjects[0] = static_cast(fbo->texture()); this->OffScreenRendering = true; this->OffScreenUseFrameBuffer = true; this->Modified(); } diff --git a/Modules/OpenViewCore/src/QVTKInternalOpenglRenderWindow.h b/Modules/OpenViewCore/src/vtkInternalOpenGLRenderWindow.h similarity index 95% rename from Modules/OpenViewCore/src/QVTKInternalOpenglRenderWindow.h rename to Modules/OpenViewCore/src/vtkInternalOpenGLRenderWindow.h index 8365714843..0500d7aa0c 100644 --- a/Modules/OpenViewCore/src/QVTKInternalOpenglRenderWindow.h +++ b/Modules/OpenViewCore/src/vtkInternalOpenGLRenderWindow.h @@ -1,45 +1,46 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __vtkInternalOpenGLRenderWindow_h #define __vtkInternalOpenGLRenderWindow_h #include #include #include class QVTKFramebufferObjectRenderer; +//! Part of Qml rendering prototype, see QmlMitkRenderWindowItem. class vtkInternalOpenGLRenderWindow : public vtkGenericOpenGLRenderWindow { public: static vtkInternalOpenGLRenderWindow* New(); vtkTypeMacro(vtkInternalOpenGLRenderWindow, vtkGenericOpenGLRenderWindow) virtual void OpenGLInitState(); virtual void Render(); void OpenGLEndState(); void InternalRender(); void SetFramebufferObject(QOpenGLFramebufferObject *fbo); QVTKFramebufferObjectRenderer *QtParentRenderer; protected: vtkInternalOpenGLRenderWindow(); ~vtkInternalOpenGLRenderWindow(); }; #endif diff --git a/Modules/QmlItems/include/QmlMitkRenderWindowItem.h b/Modules/QmlItems/include/QmlMitkRenderWindowItem.h index 5b20a23a2e..db2f655065 100644 --- a/Modules/QmlItems/include/QmlMitkRenderWindowItem.h +++ b/Modules/QmlItems/include/QmlMitkRenderWindowItem.h @@ -1,92 +1,140 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __QmlMitkRenderWindowItem_h #define __QmlMitkRenderWindowItem_h #include "MitkQmlItemsExports.h" #include "QVTKQuickItem.h" #include "mitkRenderWindowBase.h" #include #include class QmlMitkStdMultiItem; +//! PROTOTYPE Qml item to integrate MITK render window into Qml application. +//! +//! See diagram qml-classes-mitk.jpg for an overall picture of involved classes. +//! +//! The structure implemented in these classes follows the framebuffer approach +//! described in +//! http://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html#mixing-scene-graph-and-opengl +//! +//! That is, MITK/VTK rendering is happening inside Qml's rendering thread and +//! in parallel to Qt's main thread. The rendering outputs to a GL framebuffer +//! that is used as a texture within the Qml scene. +//! Both Qt and VTK impose their structure on how rendering should be called, +//! see the two classes QVTKFramebufferObjectRenderer and vtkInternalOpenglRenderWindow. +//! +//! As in Qt widget based render windows, QmlMitkRenderWindowItem derives from +//! a Qt/VTK-integrating render window and adds its VtkPropRenderer to the VTK +//! scene. This VtkPropRenderer collects and organizes all MITK mappers and +//! makes them render when VTK asks to. +//! +//! Challenges for future development +//! * cleanup: example grew historically, structure could perhaps be simplified, +//! responsibilities could be clearer. +//! * thread-safety: this example manages execution of MITK events within +//! the Qt rendering thread. This allows access to VTK's GL context (e.g. for picking). +//! An inherent risk are collisions on data structures around +//! DataStorage: when rendering is threaded, the application +//! could modify data storage elements that are currently being +//! accessed by rendering. Working this out would probably +//! require thread-safety on multiple levels: DataStorage hierarchy, +//! DataNode methods, property (lists), and finally BaseData +//! and all its derived classes. +//! class MITKQMLITEMS_EXPORT QmlMitkRenderWindowItem : public QVTKQuickItem, public mitk::RenderWindowBase { Q_OBJECT Q_PROPERTY(int viewType READ getViewType WRITE setViewType NOTIFY viewTypeChanged); Q_PROPERTY(QmlMitkStdMultiItem* multiItem READ getMultiItem WRITE setMultiItem NOTIFY multiItemChanged); private: QmlMitkStdMultiItem* m_multiItem; vtkSmartPointer m_annotation; vtkSmartPointer m_rectangle; int m_viewType; public: static QmlMitkRenderWindowItem* instance; static QmlMitkRenderWindowItem* GetInstanceForVTKRenderWindow( vtkRenderWindow* rw ); QmlMitkRenderWindowItem(QQuickItem* parent = 0, const QString& name = "QML render window", mitk::VtkPropRenderer* renderer = NULL, mitk::RenderingManager* renderingManager = NULL); ~QmlMitkRenderWindowItem(); virtual vtkRenderWindow* GetVtkRenderWindow(); virtual vtkRenderWindowInteractor* GetVtkRenderWindowInteractor(); void SetDataStorage(mitk::DataStorage::Pointer storage); void InitView( mitk::BaseRenderer::MapperSlotId mapperID, mitk::SliceNavigationController::ViewDirection viewDirection ); virtual bool prepareForRender(); virtual void cleanupAfterRender(); void createPlaneNode(); void setMultiItem(QmlMitkStdMultiItem* multiItem); QmlMitkStdMultiItem* getMultiItem(); void setDecorationProperties(std::string text, mitk::Color color); void setViewType(int type); int getViewType(); void geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry); virtual void mousePressEvent(QMouseEvent* e); virtual void mouseReleaseEvent(QMouseEvent* e); virtual void mouseMoveEvent(QMouseEvent* e); virtual void wheelEvent(QWheelEvent* e); mitk::Point2D GetMousePosition(QMouseEvent* me) const; + mitk::Point2D GetMousePositionFlipY(QMouseEvent* me) const; mitk::Point2D GetMousePosition(QWheelEvent* we) const; + mitk::Point2D GetMousePositionFlipY(QWheelEvent* we) const; mitk::InteractionEvent::MouseButtons GetEventButton(QMouseEvent* me) const; mitk::InteractionEvent::MouseButtons GetButtonState(QMouseEvent* me) const; mitk::InteractionEvent::ModifierKeys GetModifiers(QInputEvent* me) const; mitk::InteractionEvent::MouseButtons GetButtonState(QWheelEvent* we) const; static QMap& GetInstances(); public slots: void setupView(); signals: void multiItemChanged(); void viewTypeChanged(); + +protected: + + //! Add an event to the event queue to be executed in rendering. + //! + //! Avoids collisions between rendering and event handling, + //! both of which might act on GL buffers (e.g. picking) + void QueueEvent(mitk::InteractionEvent::Pointer e); + + //! List of events that should be treated during next rendering. + std::vector m_PendingEvents; + + //! Executes (then clears) the list of unhandled UI events (m_PendingEvents). + void processPendingEvents() override; }; #endif diff --git a/Modules/QmlItems/qml-classes-mitk.jpg b/Modules/QmlItems/qml-classes-mitk.jpg new file mode 100644 index 0000000000..47647800fa Binary files /dev/null and b/Modules/QmlItems/qml-classes-mitk.jpg differ diff --git a/Modules/QmlItems/src/QmlMitkRenderWindowItem.cpp b/Modules/QmlItems/src/QmlMitkRenderWindowItem.cpp index c8ddc7ee19..445ffe5138 100644 --- a/Modules/QmlItems/src/QmlMitkRenderWindowItem.cpp +++ b/Modules/QmlItems/src/QmlMitkRenderWindowItem.cpp @@ -1,367 +1,410 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmlMitkRenderWindowItem.h" #include #include #include #include #include "mitkMouseWheelEvent.h" #include "mitkMousePressEvent.h" #include "mitkMouseMoveEvent.h" #include "mitkMouseDoubleClickEvent.h" #include "mitkMouseReleaseEvent.h" #include "mitkInteractionKeyEvent.h" #include "mitkInternalEvent.h" #include "mitkPlaneGeometryDataMapper2D.h" #include "mitkCameraController.h" #include "QmlMitkStdMultiItem.h" +#include "QVTKFramebufferObjectRenderer.h" #include QmlMitkRenderWindowItem* QmlMitkRenderWindowItem::instance = nullptr; QmlMitkRenderWindowItem* QmlMitkRenderWindowItem::GetInstanceForVTKRenderWindow(vtkRenderWindow* rw) { if (GetInstances().contains(rw)) { return GetInstances()[rw]; } return 0; } QMap& QmlMitkRenderWindowItem::GetInstances() { static QMap s_Instances; return s_Instances; } QmlMitkRenderWindowItem::QmlMitkRenderWindowItem(QQuickItem* parent, const QString& name, mitk::VtkPropRenderer* , mitk::RenderingManager* renderingManager) : QVTKQuickItem(parent) { instance = this; mitk::RenderWindowBase::Initialize(renderingManager, name.toStdString().c_str()); GetInstances()[QVTKQuickItem::GetRenderWindow()] = this; this->m_annotation = vtkSmartPointer::New(); this->m_rectangle = vtkSmartPointer::New(); } void QmlMitkRenderWindowItem::createPlaneNode() { mitk::DataStorage::Pointer m_DataStorage = mitk::RenderWindowBase::GetRenderer()->GetDataStorage(); if (m_DataStorage.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()) ); } if (this->GetRenderer()->GetSliceNavigationController()->GetDefaultViewDirection() == mitk::SliceNavigationController::Original) return; mitk::DataNode::Pointer planeNode; mitk::IntProperty::Pointer layer; mitk::PlaneGeometryDataMapper2D::Pointer mapper = mitk::PlaneGeometryDataMapper2D::New(); layer = mitk::IntProperty::New(1000); planeNode = this->GetRenderer()->GetCurrentWorldPlaneGeometryNode(); planeNode->SetProperty("visible", mitk::BoolProperty::New(true)); planeNode->SetProperty("name", mitk::StringProperty::New("plane")); planeNode->SetProperty("isPlane", mitk::BoolProperty::New(true)); planeNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); planeNode->SetProperty("helper object", mitk::BoolProperty::New(true)); planeNode->SetProperty("layer", layer); planeNode->SetMapper(mitk::BaseRenderer::Standard2D, mapper); switch (this->GetRenderer()->GetSliceNavigationController()->GetDefaultViewDirection()) { case mitk::SliceNavigationController::Axial: planeNode->SetColor(0.88, 0.35, 0.27); break; case mitk::SliceNavigationController::Sagittal: planeNode->SetColor(0.25, 0.7, 0.35); break; case mitk::SliceNavigationController::Frontal: planeNode->SetColor(0.01, 0.31, 0.67); break; default: planeNode->SetColor(1.0, 1.0, 0.0); } } void QmlMitkRenderWindowItem::setViewType(int viewType) { this->m_viewType = viewType; emit this->viewTypeChanged(); } void QmlMitkRenderWindowItem::setDecorationProperties(std::string text, mitk::Color color) { this->m_annotation->SetText(0, text.c_str()); this->m_annotation->SetMaximumFontSize(12); this->m_annotation->GetTextProperty()->SetColor( color[0],color[1],color[2] ); if(!this->GetRenderer()->GetVtkRenderer()->HasViewProp(this->m_annotation)) { this->GetRenderer()->GetVtkRenderer()->AddViewProp(this->m_annotation); } this->m_rectangle->SetColor(color[0],color[1],color[2]); if(!this->GetRenderer()->GetVtkRenderer()->HasViewProp(this->m_rectangle)) { this->GetRenderer()->GetVtkRenderer()->AddViewProp(this->m_rectangle); } } void QmlMitkRenderWindowItem::setupView() { switch (this->m_viewType) { case 0: this->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); this->GetRenderer()->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); break; case 1: this->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); this->GetRenderer()->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Frontal); break; case 2: this->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); this->GetRenderer()->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); break; case 3: this->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); this->GetRenderer()->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Original); break; default: break; } } void QmlMitkRenderWindowItem::setMultiItem(QmlMitkStdMultiItem* multiItem) { if(this->m_multiItem == multiItem) return; this->m_multiItem = multiItem; this->m_multiItem->registerViewerItem(this); } QmlMitkStdMultiItem* QmlMitkRenderWindowItem::getMultiItem() { return this->m_multiItem; } void QmlMitkRenderWindowItem::SetDataStorage(mitk::DataStorage::Pointer storage) { this->GetRenderer()->SetDataStorage(storage); } mitk::Point2D QmlMitkRenderWindowItem::GetMousePosition(QMouseEvent* me) const { qreal ratio = this->window()->effectiveDevicePixelRatio(); mitk::Point2D point; point[0] = me->x()*ratio; point[1] = me->y()*ratio; return point; } +mitk::Point2D QmlMitkRenderWindowItem::GetMousePositionFlipY(QMouseEvent* me) const +{ + mitk::Point2D point = GetMousePosition(me); + point[1] = const_cast(this)->GetRenderer()->GetSizeY() - point[1]; + return point; +} + mitk::Point2D QmlMitkRenderWindowItem::GetMousePosition(QWheelEvent* we) const { qreal ratio = this->window()->effectiveDevicePixelRatio(); mitk::Point2D point; point[0] = we->x()*ratio; point[1] = we->y()*ratio; return point; } +mitk::Point2D QmlMitkRenderWindowItem::GetMousePositionFlipY(QWheelEvent* we) const +{ + mitk::Point2D point = GetMousePosition(we); + point[1] = const_cast(this)->GetRenderer()->GetSizeY() - point[1]; + return point; +} + mitk::InteractionEvent::MouseButtons QmlMitkRenderWindowItem::GetEventButton(QMouseEvent* me) const { mitk::InteractionEvent::MouseButtons eventButton; switch (me->button()) { case Qt::LeftButton: eventButton = mitk::InteractionEvent::LeftMouseButton; break; case Qt::RightButton: eventButton = mitk::InteractionEvent::RightMouseButton; break; case Qt::MidButton: eventButton = mitk::InteractionEvent::MiddleMouseButton; break; default: eventButton = mitk::InteractionEvent::NoButton; break; } return eventButton; } mitk::InteractionEvent::MouseButtons QmlMitkRenderWindowItem::GetButtonState(QMouseEvent* me) const { mitk::InteractionEvent::MouseButtons buttonState = mitk::InteractionEvent::NoButton; if (me->buttons() & Qt::LeftButton) { buttonState = buttonState | mitk::InteractionEvent::LeftMouseButton; } if (me->buttons() & Qt::RightButton) { buttonState = buttonState | mitk::InteractionEvent::RightMouseButton; } if (me->buttons() & Qt::MidButton) { buttonState = buttonState | mitk::InteractionEvent::MiddleMouseButton; } return buttonState; } mitk::InteractionEvent::ModifierKeys QmlMitkRenderWindowItem::GetModifiers(QInputEvent* me) const { mitk::InteractionEvent::ModifierKeys modifiers = mitk::InteractionEvent::NoKey; if (me->modifiers() & Qt::ALT) { modifiers = modifiers | mitk::InteractionEvent::AltKey; } if (me->modifiers() & Qt::CTRL) { modifiers = modifiers | mitk::InteractionEvent::ControlKey; } if (me->modifiers() & Qt::SHIFT) { modifiers = modifiers | mitk::InteractionEvent::ShiftKey; } return modifiers; } mitk::InteractionEvent::MouseButtons QmlMitkRenderWindowItem::GetButtonState(QWheelEvent* we) const { mitk::InteractionEvent::MouseButtons buttonState = mitk::InteractionEvent::NoButton; if (we->buttons() & Qt::LeftButton) { buttonState = buttonState | mitk::InteractionEvent::LeftMouseButton; } if (we->buttons() & Qt::RightButton) { buttonState = buttonState | mitk::InteractionEvent::RightMouseButton; } if (we->buttons() & Qt::MidButton) { buttonState = buttonState | mitk::InteractionEvent::MiddleMouseButton; } return buttonState; } int QmlMitkRenderWindowItem::getViewType() { return this->m_viewType; } void QmlMitkRenderWindowItem::mousePressEvent(QMouseEvent* me) { - mitk::Point2D mousePosition = GetMousePosition(me); - //mousePosition[1] = this->GetRenderer()->GetSizeY() - mousePosition[1]; + mitk::Point2D mousePosition = GetMousePositionFlipY(me); - mitk::MousePressEvent::Pointer mPressEvent = - mitk::MousePressEvent::New(mitk::RenderWindowBase::GetRenderer(), mousePosition, GetButtonState(me), GetModifiers(me), GetEventButton(me)); + auto mitkEvent = mitk::MousePressEvent::New(mitk::RenderWindowBase::GetRenderer(), + mousePosition, + GetButtonState(me), + GetModifiers(me), + GetEventButton(me)); + + QueueEvent(mitkEvent.GetPointer()); - mitk::RenderWindowBase::HandleEvent(mPressEvent.GetPointer()); QVTKQuickItem::mousePressEvent(me); } void QmlMitkRenderWindowItem::mouseReleaseEvent(QMouseEvent* me) { - mitk::Point2D mousePosition = GetMousePosition(me); - //mousePosition[1] = this->GetRenderer()->GetSizeY() - mousePosition[1]; + mitk::Point2D mousePosition = GetMousePositionFlipY(me); + + auto mitkEvent = mitk::MouseReleaseEvent::New(mitk::RenderWindowBase::GetRenderer(), + mousePosition, + GetButtonState(me), + GetModifiers(me), + GetEventButton(me)); - mitk::MouseReleaseEvent::Pointer mReleaseEvent = - mitk::MouseReleaseEvent::New(mitk::RenderWindowBase::GetRenderer(), mousePosition, GetButtonState(me), GetModifiers(me), GetEventButton(me)); + QueueEvent(mitkEvent.GetPointer()); - mitk::RenderWindowBase::HandleEvent(mReleaseEvent.GetPointer()); QVTKQuickItem::mouseReleaseEvent(me); } void QmlMitkRenderWindowItem::mouseMoveEvent(QMouseEvent* me) { - mitk::Point2D mousePosition = GetMousePosition(me); - //mousePosition[1] = this->GetRenderer()->GetSizeY() - mousePosition[1]; + mitk::Point2D mousePosition = GetMousePositionFlipY(me); - mitk::MouseMoveEvent::Pointer mMoveEvent = - mitk::MouseMoveEvent::New(mitk::RenderWindowBase::GetRenderer(), mousePosition, GetButtonState(me), GetModifiers(me)); + auto mitkEvent = mitk::MouseMoveEvent::New(mitk::RenderWindowBase::GetRenderer(), + mousePosition, + GetButtonState(me), + GetModifiers(me)); + + QueueEvent(mitkEvent.GetPointer()); - mitk::RenderWindowBase::HandleEvent(mMoveEvent.GetPointer()); QVTKQuickItem::mouseMoveEvent(me); } void QmlMitkRenderWindowItem::wheelEvent(QWheelEvent *we) { - mitk::Point2D mousePosition = GetMousePosition(we); + mitk::Point2D mousePosition = GetMousePositionFlipY(we); + + auto mitkEvent = mitk::MouseWheelEvent::New(mitk::RenderWindowBase::GetRenderer(), + mousePosition, + GetButtonState(we), + GetModifiers(we), + we->delta()); - mitk::MouseWheelEvent::Pointer mWheelEvent = - mitk::MouseWheelEvent::New(mitk::RenderWindowBase::GetRenderer(), mousePosition, GetButtonState(we), GetModifiers(we), we->delta()); + QueueEvent(mitkEvent.GetPointer()); - mitk::RenderWindowBase::HandleEvent(mWheelEvent.GetPointer()); QVTKQuickItem::wheelEvent(we); } bool QmlMitkRenderWindowItem::prepareForRender() { mitk::VtkPropRenderer *vPR = dynamic_cast(mitk::BaseRenderer::GetInstance(this->GetRenderWindow())); if (vPR) { vPR->PrepareRender(); } mitk::RenderWindowBase::GetRenderer()->ForceImmediateUpdate(); return true; } void QmlMitkRenderWindowItem::cleanupAfterRender() { } void QmlMitkRenderWindowItem::geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry) { QVTKQuickItem::geometryChanged(newGeometry, oldGeometry); mitk::BaseRenderer::GetInstance(this->GetRenderWindow())->GetCameraController()->Fit(); } QmlMitkRenderWindowItem::~QmlMitkRenderWindowItem() { this->Destroy(); } vtkRenderWindow* QmlMitkRenderWindowItem::GetVtkRenderWindow() { return QVTKQuickItem::GetRenderWindow(); } vtkRenderWindowInteractor* QmlMitkRenderWindowItem::GetVtkRenderWindowInteractor() { return QVTKQuickItem::GetInteractor(); } + +void QmlMitkRenderWindowItem::QueueEvent(mitk::InteractionEvent::Pointer e) +{ + m_PendingEvents.push_back(e); +} + +// To be called while rendering is blocked (in Qml sync methods) +// so picking can access a "still image" and data to be rendered +// can be modified by interaction +void QmlMitkRenderWindowItem::processPendingEvents() +{ + for (auto e : m_PendingEvents) { + mitk::RenderWindowBase::HandleEvent(e); + } + m_PendingEvents.clear(); +}