|
#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"); |
|
} |
Just a quick note. In the render() method of QVTKFramebufferObjectRenderer, you should also call the method start(). So it should look something like this:
m_win->Start();
m_win->Render();
The call to the Start() method makes sure all important OpenGL options are set for VTK and also computes the size of the render window (without that, the app will crash as soon as you resize the application window).
I had an issue when I exported QSG_VISUALIZE=overdraw (see at the end of http://doc.qt.io/qt-5.5/qtquick-visualcanvas-scenegraph-renderer.html). When running the code with my NVIDIA 960M it would crash after a few seconds. If the code was running on the Intel 4600, it works ok. This troubled me for a while. Adding the call to the Start() method solved it. And looking at the implementation of it, it definitely should be called before rendering.