QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsoffscreen3dengine.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsoffscreen3dengine.cpp
3  --------------------------------------
4  Date : July 2018
5  Copyright : (C) 2018 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsoffscreen3dengine.h"
17 
18 #include <QOffscreenSurface>
19 #include <QSurfaceFormat>
20 #include <QOpenGLFunctions>
21 
22 #include <Qt3DCore/QAspectEngine>
23 #include <Qt3DLogic/QLogicAspect>
24 #include <Qt3DRender/QCamera>
25 #include <Qt3DRender/QCameraSelector>
26 #include <Qt3DRender/QClearBuffers>
27 #include <Qt3DRender/QRenderAspect>
28 #include <Qt3DRender/QRenderCapture>
29 #include <Qt3DRender/QRenderSettings>
30 #include <Qt3DRender/QRenderTarget>
31 #include <Qt3DRender/QRenderTargetOutput>
32 #include <Qt3DRender/QRenderTargetSelector>
33 #include <Qt3DRender/QRenderSurfaceSelector>
34 #include <Qt3DRender/QTexture>
35 #include <Qt3DRender/QViewport>
36 #include <QtGui/QOpenGLContext>
37 
38 
40 {
41  // Set up the default OpenGL surface format.
42  QSurfaceFormat format;
43 
44  // by default we get just some older version of OpenGL from the system,
45  // but for 3D lines we use "primitive restart" functionality supported in OpenGL >= 3.1
46  // Qt3DWindow uses this - requesting OpenGL 4.3 - so let's request the same version.
47 #ifdef QT_OPENGL_ES_2
48  format.setRenderableType( QSurfaceFormat::OpenGLES );
49 #else
50  if ( QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL )
51  {
52  format.setVersion( 4, 3 );
53  format.setProfile( QSurfaceFormat::CoreProfile );
54  }
55 #endif
56 
57  format.setMajorVersion( 3 );
58  format.setDepthBufferSize( 32 ); // TODO: or 24? (used by QWindow3D)
59  format.setSamples( 8 );
60  QSurfaceFormat::setDefaultFormat( format );
61 
62  // Set up a camera to point at the shapes.
63  mCamera = new Qt3DRender::QCamera;
64  mCamera->lens()->setPerspectiveProjection( 45.0f, float( mSize.width() ) / float( mSize.height() ), 0.1f, 1000.0f );
65  mCamera->setPosition( QVector3D( 0, 0, 20.0f ) );
66  mCamera->setUpVector( QVector3D( 0, 1, 0 ) );
67  mCamera->setViewCenter( QVector3D( 0, 0, 0 ) );
68 
69  // Set up the engine and the aspects that we want to use.
70  mAspectEngine = new Qt3DCore::QAspectEngine();
71  mRenderAspect = new Qt3DRender::QRenderAspect( Qt3DRender::QRenderAspect::Threaded ); // Only threaded mode seems to work right now.
72  mLogicAspect = new Qt3DLogic::QLogicAspect();
73 
74  mAspectEngine->registerAspect( mRenderAspect );
75  mAspectEngine->registerAspect( mLogicAspect );
76 
77  // Create the root entity of the engine.
78  // This is not the same as the 3D scene root: the QRenderSettings
79  // component must be held by the root of the QEntity tree,
80  // so it is added to this one. The 3D scene is added as a subtree later,
81  // in setRootEntity().
82  mRoot = new Qt3DCore::QEntity();
83  mRenderSettings = new Qt3DRender::QRenderSettings( mRoot );
84  mRoot->addComponent( mRenderSettings );
85 
86  mCamera->setParent( mRoot );
87 
88  // Create the offscreen frame graph, which will manage all of the resources required
89  // for rendering without a QWindow.
90  createFrameGraph();
91 
92  // Set this frame graph to be in use.
93  // the render settings also sets itself as the parent of mSurfaceSelector
94  mRenderSettings->setActiveFrameGraph( mSurfaceSelector );
95 
96  // Set the root entity of the engine. This causes the engine to begin running.
97  mAspectEngine->setRootEntity( Qt3DCore::QEntityPtr( mRoot ) );
98 
99 }
100 
102 {
103  delete mAspectEngine;
104  delete mOffscreenSurface;
105 }
106 
108 {
109  mSize = s;
110 
111  mTexture->setSize( mSize.width(), mSize.height() );
112  mDepthTexture->setSize( mSize.width(), mSize.height() );
113  mSurfaceSelector->setExternalRenderTargetSize( mSize );
114 
115  mCamera->setAspectRatio( float( mSize.width() ) / float( mSize.height() ) );
116 }
117 
118 void QgsOffscreen3DEngine::setClearColor( const QColor &color )
119 {
120  mClearBuffers->setClearColor( color );
121 }
122 
124 {
125  // TODO
126  Q_UNUSED( enabled )
127 }
128 
129 void QgsOffscreen3DEngine::createRenderTarget()
130 {
131  mTextureTarget = new Qt3DRender::QRenderTarget;
132 
133  // The lifetime of the objects created here is managed
134  // automatically, as they become children of this object.
135 
136  // Create a render target output for rendering color.
137  mTextureOutput = new Qt3DRender::QRenderTargetOutput( mTextureTarget );
138  mTextureOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Color0 );
139 
140  // Create a texture to render into.
141  mTexture = new Qt3DRender::QTexture2D( mTextureOutput );
142  mTexture->setSize( mSize.width(), mSize.height() );
143  mTexture->setFormat( Qt3DRender::QAbstractTexture::RGB8_UNorm );
144  mTexture->setMinificationFilter( Qt3DRender::QAbstractTexture::Linear );
145  mTexture->setMagnificationFilter( Qt3DRender::QAbstractTexture::Linear );
146 
147  // Hook the texture up to our output, and the output up to this object.
148  mTextureOutput->setTexture( mTexture );
149  mTextureTarget->addOutput( mTextureOutput );
150 
151  mDepthTextureOutput = new Qt3DRender::QRenderTargetOutput( mTextureTarget );
152  mDepthTextureOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Depth );
153  mDepthTexture = new Qt3DRender::QTexture2D( mDepthTextureOutput );
154  mDepthTexture->setSize( mSize.width(), mSize.height() );
155  mDepthTexture->setFormat( Qt3DRender::QAbstractTexture::DepthFormat );
156  mDepthTexture->setMinificationFilter( Qt3DRender::QAbstractTexture::Linear );
157  mDepthTexture->setMagnificationFilter( Qt3DRender::QAbstractTexture::Linear );
158  mDepthTexture->setComparisonFunction( Qt3DRender::QAbstractTexture::CompareLessEqual );
159  mDepthTexture->setComparisonMode( Qt3DRender::QAbstractTexture::CompareRefToTexture );
160  // Hook up the depth texture
161  mDepthTextureOutput->setTexture( mDepthTexture );
162  mTextureTarget->addOutput( mDepthTextureOutput );
163 }
164 
165 void QgsOffscreen3DEngine::createFrameGraph()
166 {
167  // Firstly, create the offscreen surface. This will take the place
168  // of a QWindow, allowing us to render our scene without one.
169  mOffscreenSurface = new QOffscreenSurface();
170  mOffscreenSurface->setFormat( QSurfaceFormat::defaultFormat() );
171  mOffscreenSurface->create();
172 
173  // Hook it up to the frame graph.
174  mSurfaceSelector = new Qt3DRender::QRenderSurfaceSelector( mRenderSettings );
175  mSurfaceSelector->setSurface( mOffscreenSurface );
176  mSurfaceSelector->setExternalRenderTargetSize( mSize );
177 
178  // Create a texture to render into. This acts as the buffer that
179  // holds the rendered image.
180  mRenderTargetSelector = new Qt3DRender::QRenderTargetSelector( mSurfaceSelector );
181  createRenderTarget();
182  // the target selector also sets itself as the parent of mTextureTarget
183  mRenderTargetSelector->setTarget( mTextureTarget );
184 
185  // Create a node used for clearing the required buffers.
186  mClearBuffers = new Qt3DRender::QClearBuffers( mRenderTargetSelector );
187  mClearBuffers->setClearColor( QColor( 100, 100, 100, 255 ) );
188  mClearBuffers->setBuffers( Qt3DRender::QClearBuffers::ColorDepthBuffer );
189 
190  // Create a viewport node. The viewport here just covers the entire render area.
191  mViewport = new Qt3DRender::QViewport( mRenderTargetSelector );
192  mViewport->setNormalizedRect( QRectF( 0.0, 0.0, 1.0, 1.0 ) );
193 
194  // Create a camera selector node, and tell it to use the camera we've ben given.
195  mCameraSelector = new Qt3DRender::QCameraSelector( mViewport );
196  mCameraSelector->setCamera( mCamera );
197 
198  // Add a render capture node to the frame graph.
199  // This is set as the next child of the render target selector node,
200  // so that the capture will be taken from the specified render target
201  // once all other rendering operations have taken place.
202  mRenderCapture = new Qt3DRender::QRenderCapture( mRenderTargetSelector );
203 }
204 
205 void QgsOffscreen3DEngine::setRootEntity( Qt3DCore::QEntity *root )
206 {
207  // Make sure any existing scene root is unparented.
208  if ( mSceneRoot )
209  {
210  mSceneRoot->setParent( static_cast<Qt3DCore::QNode *>( nullptr ) );
211  }
212 
213  // Parent the incoming scene root to our current root entity.
214  mSceneRoot = root;
215  mSceneRoot->setParent( mAspectEngine->rootEntity().data() );
216 }
217 
218 Qt3DRender::QRenderSettings *QgsOffscreen3DEngine::renderSettings()
219 {
220  return mRenderSettings;
221 }
222 
223 Qt3DRender::QCamera *QgsOffscreen3DEngine::camera()
224 {
225  return mCamera;
226 }
227 
229 {
230  return mSize;
231 }
232 
234 {
235  return mOffscreenSurface;
236 }
237 
239 {
240  if ( mReply )
241  {
242  qDebug() << "already having a pending capture, skipping";
243  return;
244  }
245  mReply = mRenderCapture->requestCapture();
246  connect( mReply, &Qt3DRender::QRenderCaptureReply::completed, this, [ = ]
247  {
248  QImage image = mReply->image();
249  mReply->deleteLater();
250  mReply = nullptr;
251  emit imageCaptured( image );
252  } );
253 }
QgsOffscreen3DEngine::~QgsOffscreen3DEngine
~QgsOffscreen3DEngine() override
Definition: qgsoffscreen3dengine.cpp:101
QgsOffscreen3DEngine::setRootEntity
void setRootEntity(Qt3DCore::QEntity *root) override
Sets root entity of the 3D scene.
Definition: qgsoffscreen3dengine.cpp:205
QgsOffscreen3DEngine::setClearColor
void setClearColor(const QColor &color) override
Sets background color of the scene.
Definition: qgsoffscreen3dengine.cpp:118
QgsOffscreen3DEngine::requestCaptureImage
void requestCaptureImage() override
Starts a request for an image rendered by the engine.
Definition: qgsoffscreen3dengine.cpp:238
QgsOffscreen3DEngine::camera
Qt3DRender::QCamera * camera() override
Returns pointer to the engine's camera entity.
Definition: qgsoffscreen3dengine.cpp:223
QgsAbstract3DEngine::imageCaptured
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
QgsOffscreen3DEngine::setSize
void setSize(QSize s)
Sets the size of the rendering area (in pixels)
Definition: qgsoffscreen3dengine.cpp:107
QgsOffscreen3DEngine::renderSettings
Qt3DRender::QRenderSettings * renderSettings() override
Returns access to the engine's render settings (the frame graph can be accessed from here)
Definition: qgsoffscreen3dengine.cpp:218
QgsOffscreen3DEngine::surface
QSurface * surface() const override
Returns the surface of the engine.
Definition: qgsoffscreen3dengine.cpp:233
QgsOffscreen3DEngine::QgsOffscreen3DEngine
QgsOffscreen3DEngine()
Definition: qgsoffscreen3dengine.cpp:39
QgsOffscreen3DEngine::setFrustumCullingEnabled
void setFrustumCullingEnabled(bool enabled) override
Sets whether frustum culling is enabled (this should make rendering faster by not rendering entities ...
Definition: qgsoffscreen3dengine.cpp:123
qgsoffscreen3dengine.h
QgsOffscreen3DEngine::size
QSize size() const override
Returns size of the engine's rendering area in pixels.
Definition: qgsoffscreen3dengine.cpp:228