Skip to content

Instantly share code, notes, and snippets.

@elmewo
Forked from nocnokneo/CMakeLists.txt
Last active August 29, 2015 14:11
Show Gist options
  • Save elmewo/02d37e0aba03fb3e257d to your computer and use it in GitHub Desktop.
Save elmewo/02d37e0aba03fb3e257d to your computer and use it in GitHub Desktop.

Integrating VTK into Qt Quick 2

This is a minimal proof-of-concept attempt at integrating VTK into a Qt Quick scene by rendering VTK to an OpenGL Framebuffer Object (FBO) and displaying that FBO in the QSG as a texture. This is a more robust integration approach than using the QSG beforeRendering or afterRendering signals (for example, as used by OpenView Core)

Build Instructions

Requires Qt 5.2 or greater. Tested configurations:

  • RHEL6, VTK 5.10, Qt 5.2.1
  • Ubuntu 12.04, VTK 6.1, Qt 5.3.0
  • Mac OS 10.9,VTK 6.1, Qt 5.2.1
git clone https://gist.github.com/c3fb01bb7ecaf437f7d6.git VtkFboInQtQuick
mkdir VtkFboInQtQuick-build
cd VtkFboInQtQuick-build

# may need to add: -DVTK_DIR:PATH=<VTK_DIR> where <VTK_DIR> is the
# path to the directory that contains VTKConfig.cmake
cmake ../VtkFboInQtQuick

make
./VtkFboInQtQuick
cmake_minimum_required(VERSION 2.8.11)
project(VtkFboInQtQuick)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
find_package(Qt5Quick REQUIRED)
configure_file(VtkFboInQtQuickConfig.h.in ${PROJECT_BINARY_DIR}/VtkFboInQtQuickConfig.h)
add_executable(${PROJECT_NAME}
main.cpp
QVTKFramebufferObjectItem.cpp
main.qml
)
target_link_libraries(${PROJECT_NAME} ${VTK_LIBRARIES} Qt5::Quick)
#include "VtkFboInQtQuickConfig.h"
#include "QVTKFramebufferObjectItem.h"
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkRendererCollection.h>
#include <vtkCamera.h>
#include <vtkProperty.h>
#include <QGuiApplication>
#include <QQuickView>
#include <QList>
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
qmlRegisterType<QVTKFrameBufferObjectItem>("VtkQuick", 1, 0, "VtkRenderWindow");
QQuickView view;
view.setSource(QUrl(PROJECT_SOURCE_DIR "/main.qml"));
QList<QVTKFrameBufferObjectItem*> vtkItems = view.rootObject()->findChildren<QVTKFrameBufferObjectItem*>();
// For demonstration: Add a cone to the scene of each QVTKFrameBufferObjectItem
Q_FOREACH(QVTKFrameBufferObjectItem *vtkItem, vtkItems)
{
vtkGenericOpenGLRenderWindow *rw = vtkItem->GetRenderWindow();
// Create a renderer and add it to the render window
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
rw->AddRenderer(renderer);
vtkSmartPointer<vtkConeSource> polyDataSource = vtkSmartPointer<vtkConeSource>::New();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
mapper->SetInputConnection(polyDataSource->GetOutputPort());
actor->SetMapper(mapper);
actor->GetProperty()->SetOpacity(0.5); // demonstrate support for translucent VTK objects
renderer->AddActor(actor);
}
view.setResizeMode( QQuickView::SizeRootObjectToView );
view.show();
return app.exec();
}
import QtQuick 2.0
import VtkQuick 1.0
Rectangle {
id: root
width: 800
height: 600
color: "yellow"
Rectangle {
id : vtkRenderWindowContainer
anchors.fill: parent
anchors.margins: 20
border.width: 4
border.color: "black"
color: "blue"
VtkRenderWindow {
id: vtkRenderWindow
anchors.fill: parent
property real flipAngle: mouseArea.containsMouse ? 0 : 180
Behavior on flipAngle { NumberAnimation {} }
transform: Rotation {
origin { x: vtkRenderWindow.width/2; y: vtkRenderWindow.height/2 }
axis { x: 0; y: 1; z: 0 }
angle: vtkRenderWindow.flipAngle
}
}
}
Rectangle {
id: reactiveRect
color: "mintcream"
opacity: 0.8
radius: 10
border.width: 5
border.color: "mintcream"
width: 600
height: 200
anchors.centerIn: vtkRenderWindowContainer
rotation: mouseArea.containsMouse ? -4 : 4
Behavior on rotation { NumberAnimation {} }
Text {
id: label
anchors.fill: parent
anchors.margins: 20
color: mouseArea.containsMouse ? "deeppink" : "dodgerblue"
Behavior on color { ColorAnimation { duration: 200 } }
wrapMode: Text.WordWrap
font.pointSize: 16
font.bold: true
verticalAlignment: Text.AlignVCenter
text: "The objects inside the blue rectangle are rendered to an FBO by QVTKFrameBufferObject. Everything else is a standard Qt Quick component."
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
}
}
}
#include "QVTKFramebufferObjectItem.h"
// Use the OpenGL API abstraction from Qt instead of from VTK because vtkgl.h
// and other Qt OpenGL-related headers do not play nice when included in the
// same compilation unit
#include <QOpenGLFunctions>
#include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkObjectFactory.h>
#include <vtkRendererCollection.h>
#include <vtkCamera.h>
class QVTKFramebufferObjectRenderer;
class vtkInternalOpenGLRenderWindow : public vtkGenericOpenGLRenderWindow, protected QOpenGLFunctions
{
public:
static vtkInternalOpenGLRenderWindow* New();
vtkTypeMacro(vtkInternalOpenGLRenderWindow, vtkGenericOpenGLRenderWindow)
virtual void OpenGLInitState()
{
Superclass::OpenGLInitState();
// Before any of the gl* functions in QOpenGLFunctions are called for a
// given OpenGL context, an initialization must be run within that context
this->MakeCurrent();
initializeOpenGLFunctions();
glUseProgram(0); // Shouldn't Superclass::OpenGLInitState() handle this?
glDisable(GL_DEPTH_TEST); // depth buffer fighting between the cone and the backround without this
glDisable(GL_BLEND); // doesn't seem crucial (?) but it is one of the differnces that showed up in apitrace analysis
}
// Override to use deferred rendering - Tell the QSG that we need to
// be rendered which will then, at the appropriate time, call
// InternalRender to do the actual OpenGL rendering.
virtual void Render();
// Do the actual OpenGL rendering
void InternalRender()
{
Superclass::Render();
}
// Provides a convenient way to set the protected FBO ivars from an existing
// FBO that was created and owned by Qt's FBO abstraction class
// QOpenGLFramebufferObject
void SetFramebufferObject(QOpenGLFramebufferObject *fbo);
QVTKFramebufferObjectRenderer *QtParentRenderer;
protected:
vtkInternalOpenGLRenderWindow() :
QtParentRenderer(0)
{
}
~vtkInternalOpenGLRenderWindow()
{
// Prevent superclass destructors from destroying the framebuffer object.
// QOpenGLFramebufferObject owns the FBO and manages it's lifecyle.
this->OffScreenRendering = 0;
}
};
vtkStandardNewMacro(vtkInternalOpenGLRenderWindow);
class QVTKFramebufferObjectRenderer : public QQuickFramebufferObject::Renderer
{
friend class vtkInternalOpenGLRenderWindow;
public:
QVTKFramebufferObjectRenderer(vtkInternalOpenGLRenderWindow *rw) :
m_vtkRenderWindow(rw),
m_framebufferObject(0)
{
m_vtkRenderWindow->Register(NULL);
m_vtkRenderWindow->QtParentRenderer = this;
}
~QVTKFramebufferObjectRenderer()
{
m_vtkRenderWindow->QtParentRenderer = 0;
m_vtkRenderWindow->Delete();
}
virtual void synchronize(QQuickFramebufferObject * item)
{
// the first synchronize call - right before the the framebufferObject
// is created for the first time
if (!m_framebufferObject)
{
QVTKFrameBufferObjectItem *vtkItem = static_cast<QVTKFrameBufferObjectItem*>(item);
vtkItem->init();
}
}
// Called from the render thread when the GUI thread is NOT blocked
virtual void render()
{
m_vtkRenderWindow->PushState();
m_vtkRenderWindow->OpenGLInitState();
m_vtkRenderWindow->InternalRender(); // vtkXOpenGLRenderWindow renders the scene to the FBO
m_vtkRenderWindow->PopState();
// Dolly camera back and forth - FOR DEMONSTRATION PURPOSES ONLY
static int callCount = 0;
++callCount;
double dolly = 1.0 + ((callCount % 200) > 100 ? -0.001 : 0.001);
m_vtkRenderWindow->GetRenderers()->GetFirstRenderer()->GetActiveCamera()->Dolly(dolly);
this->update();
}
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size)
{
qDebug("QVTKFramebufferObjectRenderer::createFramebufferObject");
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::Depth);
m_framebufferObject = new QOpenGLFramebufferObject(size, format);
m_vtkRenderWindow->SetFramebufferObject(m_framebufferObject);
return m_framebufferObject;
}
vtkInternalOpenGLRenderWindow *m_vtkRenderWindow;
QOpenGLFramebufferObject *m_framebufferObject;
};
//
// vtkInternalOpenGLRenderWindow Definitions
//
void vtkInternalOpenGLRenderWindow::Render()
{
if (this->QtParentRenderer)
{
this->QtParentRenderer->update();
}
}
void vtkInternalOpenGLRenderWindow::SetFramebufferObject(QOpenGLFramebufferObject *fbo)
{
// QOpenGLFramebufferObject documentation states that "The color render
// buffer or texture will have the specified internal format, and will
// be bound to the GL_COLOR_ATTACHMENT0 attachment in the framebuffer
// object"
this->BackLeftBuffer = this->FrontLeftBuffer = this->BackBuffer = this->FrontBuffer =
static_cast<unsigned int>(GL_COLOR_ATTACHMENT0);
// Save GL objects by static casting to standard C types. GL* types
// are not allowed in VTK header files.
QSize fboSize = fbo->size();
this->Size[0] = fboSize.width();
this->Size[1] = fboSize.height();
this->NumberOfFrameBuffers = 1;
this->FrameBufferObject = static_cast<unsigned int>(fbo->handle());
this->DepthRenderBufferObject = 0; // static_cast<unsigned int>(depthRenderBufferObject);
this->TextureObjects[0] = static_cast<unsigned int>(fbo->texture());
this->OffScreenRendering = 1;
this->OffScreenUseFrameBuffer = 1;
this->Modified();
}
//
// QVTKFrameBufferObjectItem Definitions
//
QVTKFrameBufferObjectItem::QVTKFrameBufferObjectItem()
{
m_win = vtkInternalOpenGLRenderWindow::New();
}
QVTKFrameBufferObjectItem::~QVTKFrameBufferObjectItem()
{
m_win->Delete();
}
QQuickFramebufferObject::Renderer *QVTKFrameBufferObjectItem::createRenderer() const
{
return new QVTKFramebufferObjectRenderer(static_cast<vtkInternalOpenGLRenderWindow*>(m_win));
}
vtkGenericOpenGLRenderWindow *QVTKFrameBufferObjectItem::GetRenderWindow() const
{
return m_win;
}
void QVTKFrameBufferObjectItem::init()
{
qDebug("QVTKFrameBufferObjectItem::init");
}
#ifndef QVTKFrameBufferObjectItem_h_
#define QVTKFrameBufferObjectItem_h_
#include <QtQuick/QQuickFramebufferObject>
class vtkGenericOpenGLRenderWindow;
class QVTKFramebufferObjectRenderer;
class QVTKFrameBufferObjectItem : public QQuickFramebufferObject
{
Q_OBJECT
public:
QVTKFrameBufferObjectItem();
~QVTKFrameBufferObjectItem();
Renderer *createRenderer() const;
vtkGenericOpenGLRenderWindow* GetRenderWindow() const;
protected:
// Called once before the FBO is created for the first time. This method is
// called from render thread while the GUI thread is blocked.
virtual void init();
vtkGenericOpenGLRenderWindow *m_win;
friend class QVTKFramebufferObjectRenderer;
};
#endif
#ifndef VTKFBOINQTQUICKCONFIG_H_IN
#define VTKFBOINQTQUICKCONFIG_H_IN
#define PROJECT_SOURCE_DIR "@PROJECT_SOURCE_DIR@"
#define PROJECT_BINARY_DIR "@PROJECT_BINARY_DIR@"
#endif // VTKFBOINQTQUICKCONFIG_H_IN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment