QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgs3dmapcanvas.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgs3dmapcanvas.cpp
3 --------------------------------------
4 Date : July 2017
5 Copyright : (C) 2017 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 <Qt3DCore/QAspectEngine>
17#include <Qt3DCore/QEntity>
18#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
19#include <Qt3DCore/QCoreAspect>
20#endif
21#include <Qt3DExtras/QForwardRenderer>
22#include <Qt3DRender/QRenderSettings>
23#include <Qt3DRender/QRenderAspect>
24#include <Qt3DInput/QInputAspect>
25#include <Qt3DInput/QInputSettings>
26#include <Qt3DLogic/QLogicAspect>
27#include <Qt3DRender/QCamera>
28
29#include "qgs3dmapcanvas.h"
30
31#include <Qt3DLogic/QFrameAction>
32#include "qgs3dmapscene.h"
33#include "qgswindow3dengine.h"
34#include "qgs3dmapsettings.h"
35#include "qgs3dmaptool.h"
37
39 : m_aspectEngine( new Qt3DCore::QAspectEngine )
40 , m_renderAspect( new Qt3DRender::QRenderAspect )
41 , m_inputAspect( new Qt3DInput::QInputAspect )
42 , m_logicAspect( new Qt3DLogic::QLogicAspect )
43 , m_renderSettings( new Qt3DRender::QRenderSettings )
44 , m_defaultCamera( new Qt3DRender::QCamera )
45 , m_inputSettings( new Qt3DInput::QInputSettings )
46 , m_root( new Qt3DCore::QEntity )
47 , m_userRoot( nullptr )
48 , m_initialized( false )
49{
50 setSurfaceType( QSurface::OpenGLSurface );
51
52 // register aspects
53#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
54 m_aspectEngine->registerAspect( new Qt3DCore::QCoreAspect );
55#endif
56 m_aspectEngine->registerAspect( m_renderAspect );
57 m_aspectEngine->registerAspect( m_inputAspect );
58 m_aspectEngine->registerAspect( m_logicAspect );
59
60 m_defaultCamera->setParent( m_root );
61 m_inputSettings->setEventSource( this );
62
63 const QgsSettings setting;
64 mEngine = new QgsWindow3DEngine( this );
65
66 connect( mEngine, &QgsAbstract3DEngine::imageCaptured, this, [ = ]( const QImage & image )
67 {
68 image.save( mCaptureFileName, mCaptureFileFormat.toLocal8Bit().data() );
69 mEngine->setRenderCaptureEnabled( false );
70 emit savedAsImage( mCaptureFileName );
71 } );
72
73 setCursor( Qt::OpenHandCursor );
74 installEventFilter( this );
75}
76
78{
79 if ( mMapTool )
80 mMapTool->deactivate();
81 // make sure the scene is deleted while map settings object is still alive
82 mScene->deleteLater();
83 mScene = nullptr;
84 mMapSettings->deleteLater();
85 mMapSettings = nullptr;
86
87
88 delete m_aspectEngine;
89}
90
91void Qgs3DMapCanvas::setRootEntity( Qt3DCore::QEntity *root )
92{
93 if ( m_userRoot != root )
94 {
95 if ( m_userRoot != nullptr )
96 m_userRoot->setParent( static_cast<Qt3DCore::QNode *>( nullptr ) );
97 if ( root != nullptr )
98 root->setParent( m_root );
99 m_userRoot = root;
100 }
101}
102
103void Qgs3DMapCanvas::setActiveFrameGraph( Qt3DRender::QFrameGraphNode *activeFrameGraph )
104{
105 m_renderSettings->setActiveFrameGraph( activeFrameGraph );
106}
107
108Qt3DRender::QFrameGraphNode *Qgs3DMapCanvas::activeFrameGraph() const
109{
110 return m_renderSettings->activeFrameGraph();
111}
112
113Qt3DRender::QCamera *Qgs3DMapCanvas::camera() const
114{
115 return m_defaultCamera;
116}
117
118Qt3DRender::QRenderSettings *Qgs3DMapCanvas::renderSettings() const
119{
120 return m_renderSettings;
121}
122
123void Qgs3DMapCanvas::showEvent( QShowEvent *e )
124{
125 if ( !m_initialized )
126 {
127 m_root->addComponent( m_renderSettings );
128 m_root->addComponent( m_inputSettings );
129 m_aspectEngine->setRootEntity( Qt3DCore::QEntityPtr( m_root ) );
130
131 m_initialized = true;
132 }
133 QWindow::showEvent( e );
134}
135
136void Qgs3DMapCanvas::resizeEvent( QResizeEvent * )
137{
138 m_defaultCamera->setAspectRatio( float( width() ) / std::max( 1.f, static_cast<float>( height() ) ) );
139
140 mEngine->setSize( size() );
141}
142
144{
145 // TODO: eventually we want to get rid of this
146 Q_ASSERT( !mMapSettings );
147 Q_ASSERT( !mScene );
148
149 Qgs3DMapScene *newScene = new Qgs3DMapScene( *mapSettings, mEngine );
150
151 mEngine->setSize( size() );
152 mEngine->setRootEntity( newScene );
153
154 if ( mScene )
155 {
156 mScene->deleteLater();
157 }
158 mScene = newScene;
162
163 delete mMapSettings;
164 mMapSettings = mapSettings;
165
166 resetView();
167
168 connect( cameraController(), &QgsCameraController::setCursorPosition, this, [ = ]( QPoint point )
169 {
170 QCursor::setPos( mapToGlobal( point ) );
171 } );
174 connect( cameraController(), &QgsCameraController::navigationModeChanged, this, &Qgs3DMapCanvas::onNavigationModeChanged );
175 connect( cameraController(), &QgsCameraController::requestDepthBufferCapture, this, &Qgs3DMapCanvas::captureDepthBuffer );
176
178
179 emit mapSettingsChanged();
180}
181
183{
184 return mScene ? mScene->cameraController() : nullptr;
185}
186
188{
189 if ( !mScene )
190 return;
191
192 mScene->viewZoomFull();
193}
194
195void Qgs3DMapCanvas::setViewFromTop( const QgsPointXY &center, float distance, float rotation )
196{
197 if ( !mScene )
198 return;
199
200 const float worldX = center.x() - mMapSettings->origin().x();
201 const float worldY = center.y() - mMapSettings->origin().y();
202 mScene->cameraController()->setViewFromTop( worldX, -worldY, distance, rotation );
203}
204
205void Qgs3DMapCanvas::saveAsImage( const QString &fileName, const QString &fileFormat )
206{
207 if ( !mScene || fileName.isEmpty() )
208 return;
209
210 mCaptureFileName = fileName;
211 mCaptureFileFormat = fileFormat;
212 mEngine->setRenderCaptureEnabled( true );
213 // Setup a frame action that is used to wait until next frame
214 Qt3DLogic::QFrameAction *screenCaptureFrameAction = new Qt3DLogic::QFrameAction;
215 mScene->addComponent( screenCaptureFrameAction );
216 // Wait to have the render capture enabled in the next frame
217 connect( screenCaptureFrameAction, &Qt3DLogic::QFrameAction::triggered, this, [ = ]( float )
218 {
219 mEngine->requestCaptureImage();
220 mScene->removeComponent( screenCaptureFrameAction );
221 screenCaptureFrameAction->deleteLater();
222 } );
223}
224
225void Qgs3DMapCanvas::captureDepthBuffer()
226{
227 if ( !mScene )
228 return;
229
230 // Setup a frame action that is used to wait until next frame
231 Qt3DLogic::QFrameAction *screenCaptureFrameAction = new Qt3DLogic::QFrameAction;
232 mScene->addComponent( screenCaptureFrameAction );
233 // Wait to have the render capture enabled in the next frame
234 connect( screenCaptureFrameAction, &Qt3DLogic::QFrameAction::triggered, this, [ = ]( float )
235 {
236 mEngine->requestDepthBufferCapture();
237 mScene->removeComponent( screenCaptureFrameAction );
238 screenCaptureFrameAction->deleteLater();
239 } );
240}
241
243{
244 if ( !mScene )
245 return;
246
247 if ( tool == mMapTool )
248 return;
249
250 // For Camera Control tool
251 if ( mMapTool && !tool )
252 {
253 mScene->cameraController()->setEnabled( true );
254 setCursor( Qt::OpenHandCursor );
255 }
256 else if ( !mMapTool && tool )
257 {
258 mScene->cameraController()->setEnabled( tool->allowsCameraControls() );
259 }
260
261 if ( mMapTool )
262 mMapTool->deactivate();
263
264 mMapTool = tool;
265
266 if ( mMapTool )
267 {
268 mMapTool->activate();
269 setCursor( mMapTool->cursor() );
270 }
271
272}
273
274bool Qgs3DMapCanvas::eventFilter( QObject *watched, QEvent *event )
275{
276 if ( watched != this )
277 return false;
278
279 if ( event->type() == QEvent::ShortcutOverride )
280 {
281 // if the camera controller will handle a key event, don't allow it to propagate
282 // outside of the 3d window or it may be grabbed by a parent window level shortcut
283 // and accordingly never be received by the camera controller
284 if ( cameraController() && cameraController()->willHandleKeyEvent( static_cast< QKeyEvent * >( event ) ) )
285 {
286 event->accept();
287 return true;
288 }
289 return false;
290 }
291
292 if ( !mMapTool )
293 return false;
294
295 switch ( event->type() )
296 {
297 case QEvent::MouseButtonPress:
298 mMapTool->mousePressEvent( static_cast<QMouseEvent *>( event ) );
299 break;
300 case QEvent::MouseButtonRelease:
301 mMapTool->mouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
302 break;
303 case QEvent::MouseMove:
304 mMapTool->mouseMoveEvent( static_cast<QMouseEvent *>( event ) );
305 break;
306 case QEvent::KeyPress:
307 mMapTool->keyPressEvent( static_cast<QKeyEvent *>( event ) );
308 break;
309 default:
310 break;
311 }
312 return false;
313}
314
316{
317 if ( mTemporalController )
318 disconnect( mTemporalController, &QgsTemporalController::updateTemporalRange, this, &Qgs3DMapCanvas::updateTemporalRange );
319
320 mTemporalController = temporalController;
321 connect( mTemporalController, &QgsTemporalController::updateTemporalRange, this, &Qgs3DMapCanvas::updateTemporalRange );
322}
323
324void Qgs3DMapCanvas::updateTemporalRange( const QgsDateTimeRange &temporalrange )
325{
326 if ( !mScene )
327 return;
328
329 mMapSettings->setTemporalRange( temporalrange );
330 mScene->updateTemporal();
331}
332
333void Qgs3DMapCanvas::onNavigationModeChanged( Qgis::NavigationMode mode )
334{
335 mMapSettings->setCameraNavigationMode( mode );
336}
337
339{
340 if ( !mScene )
341 return;
342
343 mScene->setViewFrom2DExtent( extent );
344}
345
347{
348 return mScene ? mScene->viewFrustum2DExtent() : QVector<QgsPointXY>();
349}
NavigationMode
The navigation mode used by 3D cameras.
Definition: qgis.h:3410
void saveAsImage(const QString &fileName, const QString &fileFormat)
Saves the current scene as an image.
QVector< QgsPointXY > viewFrustum2DExtent()
Calculates the 2D extent viewed by the 3D camera as the vertices of the viewed trapezoid.
Qgs3DMapSettings * mapSettings()
Returns access to the 3D scene configuration.
void setTemporalController(QgsTemporalController *temporalController)
Sets the temporal controller.
void mapSettingsChanged()
Emitted when the the map setting is changed.
Qt3DRender::QCamera * camera() const
Returns the default camera of the 3D Window.
void viewed2DExtentFrom3DChanged(QVector< QgsPointXY > extent)
Emitted when the viewed 2D extent seen by the 3D camera has changed.
void fpsCountChanged(float fpsCount)
Emitted when the FPS count changes (at most every frame)
void setRootEntity(Qt3DCore::QEntity *root)
Sets the specified root entity of the scene.
void setViewFromTop(const QgsPointXY &center, float distance, float rotation=0)
Sets camera position to look down at the given point (in map coordinates) in given distance from plan...
void setActiveFrameGraph(Qt3DRender::QFrameGraphNode *activeFrameGraph)
Activates the specified activeFrameGraph.
void setMapSettings(Qgs3DMapSettings *mapSettings)
Configure map scene being displayed. Takes ownership.
void showEvent(QShowEvent *e) override
Manages the display events specified in e.
void cameraNavigationSpeedChanged(double speed)
Emitted when the camera navigation speed is changed.
Qt3DRender::QRenderSettings * renderSettings() const
Returns the render settings of the 3D Window.
~Qgs3DMapCanvas()
Destructor for Qgs3DMapCanvas.
Qt3DRender::QFrameGraphNode * activeFrameGraph() const
Returns the node of the active frame graph.
void setMapTool(Qgs3DMapTool *tool)
Sets the active map tool that will receive events from the 3D canvas.
void setViewFrom2DExtent(const QgsRectangle &extent)
Resets camera view to show the extent (top view)
Qgs3DMapCanvas()
Constructor for Qgs3DMapCanvas.
void resizeEvent(QResizeEvent *) override
Resets the aspect ratio of the 3D window.
void resetView()
Resets camera position to the default: looking down at the origin of world coordinates.
void savedAsImage(const QString &fileName)
Emitted when the 3D map canvas was successfully saved as image.
void fpsCounterEnabledChanged(bool enabled)
Emitted when the FPS counter is enabled or disabeld.
QgsCameraController * cameraController()
Returns access to the view's camera controller. Returns nullptr if the scene has not been initialized...
bool eventFilter(QObject *watched, QEvent *event) override
void viewed2DExtentFrom3DChanged(QVector< QgsPointXY > extent)
Emitted when the viewed 2D extent seen by the 3D camera has changed.
void fpsCountChanged(float fpsCount)
Emitted when the FPS count changes.
void setViewFrom2DExtent(const QgsRectangle &extent)
Resets camera view to show the extent extent (top view)
QgsCameraController * cameraController() const
Returns camera controller.
Definition: qgs3dmapscene.h:81
void updateTemporal()
Updates the temporale entities.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is activated or deactivated.
QVector< QgsPointXY > viewFrustum2DExtent() const
Calculates the 2D extent viewed by the 3D camera as the vertices of the viewed trapezoid.
void viewZoomFull()
Resets camera view to show the whole scene (top view)
void setCameraNavigationMode(Qgis::NavigationMode navigationMode)
Sets the navigation mode for the camera.
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
virtual bool allowsCameraControls() const
Whether the default mouse controls to zoom/pan/rotate camera can stay enabled while the tool is activ...
Definition: qgs3dmaptool.h:68
virtual void mousePressEvent(QMouseEvent *event)
Reimplement to handle mouse event forwarded by the parent Qgs3DMapCanvas.
virtual QCursor cursor() const
Mouse cursor to be used when the tool is active.
virtual void mouseMoveEvent(QMouseEvent *event)
Reimplement to handle mouse move event forwarded by the parent Qgs3DMapCanvas.
virtual void keyPressEvent(QKeyEvent *event)
Reimplement to handle key press event forwarded by the parent Qgs3DMapCanvas.
virtual void deactivate()
Called when map tool is being deactivated.
virtual void activate()
Called when set as currently active map tool.
virtual void mouseReleaseEvent(QMouseEvent *event)
Reimplement to handle mouse release event forwarded by the parent Qgs3DMapCanvas.
void requestCaptureImage()
Starts a request for an image rendered by the engine.
void requestDepthBufferCapture()
Starts a request for an image containing the depth buffer data of the engine.
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
void depthBufferCaptured(const QImage &image)
Emitted after a call to requestDepthBufferCapture() to return the captured depth buffer.
void setRenderCaptureEnabled(bool enabled)
Sets whether it will be possible to render to an image.
void navigationModeChanged(Qgis::NavigationMode mode)
Emitted when the navigation mode is changed using the hotkey ctrl + ~.
void requestDepthBufferCapture()
Emitted to ask for the depth buffer image.
void cameraMovementSpeedChanged(double speed)
Emitted whenever the camera movement speed is changed by the controller.
void setViewFromTop(float worldX, float worldY, float distance, float yaw=0)
Sets camera to look down towards given point in world coordinate, in given distance from plane with z...
void depthBufferCaptured(const QImage &depthImage)
Sets the depth buffer image used by the camera controller to calculate world position from a pixel's ...
void setCursorPosition(QPoint point)
Emitted when the mouse cursor position should be moved to the specified point on the map viewport.
A class to represent a 2D point.
Definition: qgspointxy.h:60
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
A rectangle specified with double values.
Definition: qgsrectangle.h:42
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
A controller base class for temporal objects, contains a signal for notifying updates of the objects ...
void updateTemporalRange(const QgsDateTimeRange &range)
Signals that a temporal range has changed and needs to be updated in all connected objects.
void setTemporalRange(const QgsDateTimeRange &range)
Sets the temporal range for the object.
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:50
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:48
void setRootEntity(Qt3DCore::QEntity *root) override
Sets root entity of the 3D scene.
void setSize(QSize s) override
Sets the size of the rendering area (in pixels)